├── .csvconverter.sample ├── .gitignore ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── csv2strings └── strings2csv ├── csv2strings.gemspec ├── lib ├── csvconverter.rb └── csvconverter │ ├── command.rb │ ├── commands │ ├── csv2strings_command.rb │ └── strings2csv_command.rb │ ├── csv2strings.rb │ ├── google_doc.rb │ └── strings2csv.rb └── test ├── csvconverter ├── commands │ ├── test_command_csv2strings.rb │ └── test_command_strings2csv.rb ├── test_bins.rb ├── test_csv2strings.rb └── test_strings2csv.rb ├── data ├── test_data.csv ├── test_data.strings ├── test_data_multiple_langs.csv ├── test_en.strings ├── test_fr.strings ├── test_with_nil.csv └── test_with_nil.strings └── test_helper.rb /.csvconverter.sample: -------------------------------------------------------------------------------- 1 | # This is a sample configuration file 2 | filenames: ["test/data/test_en.strings", "test/data/test_fr.strings"] 3 | headers: ["Constants", "English", "French"] 4 | csv_filename: "testconfig.csv" 5 | dryrun: true 6 | 7 | filename: test/data/test_data.csv 8 | langs: 9 | English: "en" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | *.csv 3 | *.strings 4 | *.lproj 5 | *.gem 6 | *~ 7 | *# 8 | #*# 9 | 10 | # Config file 11 | .csvconverter 12 | 13 | # SimpleCov 14 | coverage 15 | 16 | # ignore Gemfile.lock as http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/ 17 | # this should solve the fastercsv issue 18 | Gemfile.lock 19 | 20 | # Ruby package manager files 21 | .ruby-version 22 | .rbenv-version 23 | .rvmrc 24 | 25 | # Sublime Text 26 | *.sublime-workspace 27 | *.sublime-project 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.3 4 | - 1.9.2 5 | - 1.8.7 6 | - 2.0.0 7 | deploy: 8 | provider: rubygems 9 | api_key: 10 | secure: Xjq+v+jEU6wK4BtyfnV1elegNcxK6Ah/O99Sn9c2IlkCmJ1wxLBouqzEiSorSJ4IOMa5H2y3gwo5GXOr6Y7d8huyGrPuBeCSGqAmH77wNCIv7G+jnLiYb1sRZbtKcPW2QaN6JF81qDIelwyspMfo6/ug1qN1x323UaxZl7f7nUE= 11 | gem: csv2strings 12 | on: 13 | tags: true 14 | repo: netbe/CSV-to-iOS-Localizable.strings-converter 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in teachmehowtomakearubygem.gemspec 4 | gemspec 5 | 6 | group :test do 7 | gem 'coveralls', :require => false, :platforms => [:ruby_19, :ruby_20] 8 | end 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 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. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 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 | [![Coverage Status](https://coveralls.io/repos/netbe/CSV-to-iOS-Localizable.strings-converter/badge.png)](https://coveralls.io/r/netbe/CSV-to-iOS-Localizable.strings-converter) 4 | # Introduction 5 | This script converts a csv file of translations into iOS .strings files and vice-versa. 6 | 7 | # Setup 8 | 9 | `gem install csv2strings` 10 | 11 | # Usage 12 | 13 | * Convert csv to localizable strings files: 14 | 15 | `csv2strings ` 16 | 17 | * Convert localizable strings files to csv: 18 | 19 | `strings2csv ` 20 | 21 | * Use configuration file 22 | 23 | You can use a configuration file to hold all your commandline arguments into a file (previous versions `i18n_config.rb`). 24 | Place `.csvconverter` file (edit if needed) in the folder with your ``xx.lproj`` and call the script from there. See `.csvconverter.sample` 25 | 26 | # Contributing 27 | 28 | If you feel like it, just create a pull request with a branch like `feature/` to `develop` branch 29 | 30 | 31 | ## Development 32 | 33 | Edge version can be found on `develop` branch. 34 | 35 | Run `bundle install` to install all the dependencies. Tests are done with `Test::Unit` so run `rake test` to run all the test suite. 36 | 37 | # Todo & Known issues 38 | 39 | See GitHub issues 40 | 41 | 42 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/netbe/csv-to-ios-localizable.strings-converter/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 43 | 44 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new do |t| 4 | t.libs << "test" 5 | t.test_files = FileList['test/csvconverter/**/test_*.rb'] 6 | # t.warning = true 7 | t.verbose = true 8 | end 9 | 10 | desc "Run tests" 11 | task :default => :test 12 | -------------------------------------------------------------------------------- /bin/csv2strings: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path("../../lib", __FILE__) 3 | 4 | require 'csvconverter' 5 | require 'csvconverter/commands/csv2strings_command' 6 | 7 | CSV2StringsCommand.start 8 | -------------------------------------------------------------------------------- /bin/strings2csv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path("../../lib", __FILE__) 3 | require 'csvconverter' 4 | require 'csvconverter/commands/strings2csv_command' 5 | 6 | Strings2CSVCommand.start 7 | -------------------------------------------------------------------------------- /csv2strings.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'csv2strings' 3 | s.version = '0.2.2' 4 | s.date = '2013-10-30' 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 | 12 | s.add_dependency "thor" 13 | 14 | 15 | if RUBY_VERSION < '1.9' 16 | s.add_dependency "fastercsv" 17 | s.add_dependency "nokogiri", "= 1.5.10" 18 | s.add_dependency "google_drive", '0.3.6' 19 | s.add_dependency "orderedhash" 20 | else 21 | s.add_dependency "google_drive" 22 | end 23 | 24 | # google_drive dependency to ask for mail and password 25 | s.add_dependency "highline" 26 | 27 | s.add_development_dependency "rake" 28 | 29 | s.add_development_dependency "test-unit" 30 | s.add_development_dependency "simplecov" 31 | 32 | s.files = `git ls-files`.split("\n") 33 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 34 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 35 | s.require_path = 'lib' 36 | end 37 | -------------------------------------------------------------------------------- /lib/csvconverter.rb: -------------------------------------------------------------------------------- 1 | CSVGEM = RUBY_VERSION.match(/^[0-1]\.[0-8]\./) ? 'faster_csv' : 'csv' 2 | 3 | if RUBY_VERSION.match(/^[0-1]\.[0-8]\./) 4 | require "orderedhash" 5 | ORDERED_HASH_CLASS = OrderedHash 6 | else 7 | ORDERED_HASH_CLASS = Hash 8 | end 9 | 10 | begin 11 | require CSVGEM 12 | rescue LoadError 13 | puts "Failed to load #{CSVGEM} (ruby #{RUBY_VERSION})" 14 | puts "gem install #{CSVGEM}" 15 | abort 16 | end 17 | 18 | CSVParserClass = CSVGEM == 'csv' ? CSV : FasterCSV 19 | require "csvconverter/csv2strings" 20 | require "csvconverter/strings2csv" 21 | require "csvconverter/google_doc" 22 | -------------------------------------------------------------------------------- /lib/csvconverter/command.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | class Command < Thor 3 | include Thor::Actions 4 | class_option :verbose, :type => :boolean 5 | 6 | desc "csv_download", "Download Google Spreadsheet containing translations" 7 | method_option :gd_filename, :type => :string, :required => :true, :desc => "File to download from Google Drive" 8 | method_option :output_filename, :type => :string, :desc => "Filepath of downloaded file" 9 | def csv_download 10 | filename = options['gd_filename'] 11 | gd = GoogleDoc.new 12 | if options['output_filename'] 13 | file_path = gd.download filename.to_s, options['output_filename'] 14 | else 15 | file_path = gd.download filename.to_s 16 | end 17 | if file_path 18 | say "File '#{filename}' downloaded to '#{file_path}'" 19 | else 20 | say "Could not download the requested file: #{filename}" 21 | end 22 | file_path 23 | end 24 | 25 | private 26 | def options 27 | original_options = super 28 | return original_options unless File.exists?(".csvconverter") 29 | defaults = ::YAML::load_file(".csvconverter") || {} 30 | # add default values for options here 31 | defaults["csv_filename"] = "translations.csv" unless defaults.has_key?("csv_filename") 32 | defaults["dryrun"] = false unless defaults.has_key?("dryrun") 33 | defaults["fetch"] = false unless defaults.has_key?("fetch") 34 | defaults["keys_column"] = 0 unless defaults.has_key?("keys_column") 35 | Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options)) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/csvconverter/commands/csv2strings_command.rb: -------------------------------------------------------------------------------- 1 | require "csvconverter/command" 2 | class CSV2StringsCommand < Command 3 | default_task :csv2strings 4 | 5 | desc "CSV_FILENAME", "convert CSV file to '.strings' file" 6 | # required options but handled in method because of options read from yaml file 7 | method_option :filename, :type => :string, :desc => "CSV file (CSV_FILENAME) to convert from or name of file in Google Drive" 8 | method_option :fetch, :type => :boolean, :desc => "Download file from Google Drive" 9 | method_option :langs, :type => :hash, :aliases => "-L", :desc => "Languages to convert" 10 | # optional options 11 | method_option :excluded_states, :type => :array, :aliases => "-x", :desc => "Exclude rows with given state" 12 | method_option :state_column, :type => :numeric, :aliases => "-s", :desc => "Position of column for state if any" 13 | method_option :keys_column, :type => :numeric, :aliases => "-k", :desc => "Position of column for keys" 14 | method_option :default_lang, :type => :string, :aliases => "-l", :desc => "Default language to use for empty values if any" 15 | method_option :default_path, :type => :string, :aliases => "-p", :desc => "Path of output files" 16 | def csv2strings(filename = nil) 17 | unless filename || options.has_key?('filename') 18 | say "No value provided for required options '--filename'" 19 | help("csv2strings") 20 | exit 21 | end 22 | 23 | filename ||= options['filename'] 24 | if options['fetch'] 25 | say "Downloading file from Google Drive" 26 | filename = invoke :csv_download, nil, {"gd_filename" => filename} 27 | exit unless filename 28 | end 29 | 30 | unless options.has_key?('langs') 31 | say "No value provided for required options '--langs'" 32 | help("csv2strings") 33 | exit 34 | end 35 | 36 | args = options.dup 37 | args.delete(:langs) 38 | args.delete(:filename) 39 | converter = CSV2Strings.new(filename, options[:langs], args) 40 | say converter.csv_to_dotstrings 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /lib/csvconverter/commands/strings2csv_command.rb: -------------------------------------------------------------------------------- 1 | require "csvconverter/command" 2 | class Strings2CSVCommand < Command 3 | default_task :strings2csv 4 | 5 | desc "FILENAMES", "convert '.strings' files to CSV file" 6 | # required options but handled in method because of options read from yaml file 7 | method_option :filenames, :type => :array, :aliases => "-i", :desc => "location of strings files (FILENAMES)" 8 | # optional options 9 | method_option :csv_filename, :type => :string, :aliases => "-o", :desc => "location of output file" 10 | method_option :headers, :type => :array, :aliases => "-h", :desc => "override headers of columns, default is name of input files and 'Variables' for reference" 11 | method_option :dryrun, :type => :boolean, :aliases => "-n", :desc => "prints out content of hash without writing file" 12 | def strings2csv 13 | unless options.has_key?('filenames') 14 | say "No value provided for required options '--filenames'" 15 | help("strings2csv") 16 | return 17 | end 18 | converter = Strings2CSV.new(options) 19 | debug_values = converter.dotstrings_to_csv(!options[:dryrun]) 20 | say debug_values.inspect if options[:dryrun] 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/csvconverter/csv2strings.rb: -------------------------------------------------------------------------------- 1 | class CSV2Strings 2 | attr_accessor :csv_filename, :output_file 3 | attr_accessor :langs, :default_lang 4 | attr_accessor :default_path 5 | attr_accessor :excluded_states, :state_column, :keys_column 6 | 7 | 8 | def initialize(filename, langs, args = {}) 9 | args.merge!({ 10 | :excluded_states => [], 11 | :state_column => nil, 12 | :keys_column => 0}) 13 | 14 | @csv_filename = filename 15 | @langs = langs 16 | 17 | if !@langs.is_a?(Hash) || @langs.size == 0 18 | raise "wrong format or/and languages parameter" + @langs.inspect 19 | end 20 | @output_file = (@langs.size == 1) ? args[:output_file] : nil 21 | 22 | @default_path = args[:default_path].to_s 23 | @excluded_states = args[:excluded_states] 24 | @state_column = args[:state_column] 25 | @keys_column = args[:keys_column] 26 | @default_lang = args[:default_lang] 27 | end 28 | 29 | def create_file_from_path(file_path) 30 | path = File.dirname(file_path) 31 | FileUtils.mkdir_p path 32 | return File.new(file_path,"w") 33 | end 34 | 35 | def process_header(excludedCols, files, row, index) 36 | files[index] = [] 37 | lang_index = row[index] 38 | 39 | # create output files here 40 | if @output_file 41 | # one single file 42 | files[index] << self.create_file_from_path(@output_file) 43 | else 44 | # create one file for each languages 45 | if self.langs[lang_index].is_a?(Array) 46 | 47 | self.langs[lang_index].each do |locale| 48 | filename = self.file_path_for_locale(locale) 49 | files[index] << self.create_file_from_path(filename) 50 | end 51 | elsif self.langs[lang_index].is_a?(String) 52 | locale = self.langs[lang_index] 53 | filename = self.file_path_for_locale(locale) 54 | files[index] << self.create_file_from_path(filename) 55 | else 56 | raise "wrong format or/and languages parameter" 57 | end 58 | 59 | end 60 | end 61 | 62 | def file_path_for_locale(locale) 63 | require 'pathname' 64 | Pathname.new(self.default_path) + "#{locale}.lproj" + "Localizable.strings" 65 | end 66 | 67 | def process_value(row_value, default_value) 68 | value = row_value.nil? ? default_value : row_value 69 | value = "" if value.nil? 70 | value.gsub!(/\\*\"/, "\\\"") #escape double quotes 71 | value.gsub!(/\s*(\n|\\\s*n)\s*/, "\\n") #replace new lines with \n + strip 72 | value.gsub!(/%\s+([a-zA-Z@])([^a-zA-Z@]|$)/, "%\\1\\2") #repair string formats ("% d points" etc) 73 | value.gsub!(/([^0-9\s\(\{\[^])%/, "\\1 %") 74 | value.strip! 75 | return value 76 | end 77 | 78 | # Convert csv file to multiple Localizable.strings files for each column 79 | def csv_to_dotstrings(name = self.csv_filename) 80 | files = {} 81 | rowIndex = 0 82 | excludedCols = [] 83 | defaultCol = 0 84 | nb_translations = 0 85 | 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 | info = "Created #{files.size} files. Content: #{nb_translations} translations\n" 119 | info += "List of created files:\n" 120 | 121 | # closing I/O 122 | files.each do |key,locale_files| 123 | locale_files.each do |file| 124 | info += "#{file.path.to_s}\n" 125 | file.close 126 | end 127 | end 128 | info 129 | end # end of method 130 | 131 | end # end of class 132 | 133 | -------------------------------------------------------------------------------- /lib/csvconverter/google_doc.rb: -------------------------------------------------------------------------------- 1 | # Faraday is a dependency of google_drive, this silents the warning 2 | # see https://github.com/CocoaPods/CocoaPods/commit/f33f967427b857bf73645fd4d3f19eb05e9be0e0 3 | # This is to make sure Faraday doesn't warn the user about the `system_timer` gem missing. 4 | old_warn, $-w = $-w, nil 5 | begin 6 | require "google_drive" 7 | ensure 8 | $-w = old_warn 9 | end 10 | 11 | class GoogleDoc 12 | attr_accessor :session 13 | 14 | def authenticate 15 | # will try to get token from ~/.ruby_google_drive.token 16 | @session = GoogleDrive.saved_session 17 | end 18 | 19 | def download(requested_filename, output_filename = "translations.csv") 20 | unless @session 21 | self.authenticate 22 | end 23 | result = @session.file_by_title(requested_filename) 24 | if result.is_a? Array 25 | file = result.first 26 | else 27 | file = result 28 | end 29 | return nil unless file 30 | file.export_as_file(output_filename, "csv") 31 | return output_filename 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /lib/csvconverter/strings2csv.rb: -------------------------------------------------------------------------------- 1 | class Strings2CSV 2 | # default_lang is the the column to refer to if a value is missing 3 | # actually default_lang = default_filename 4 | attr_accessor :csv_filename, :headers, :filenames, :default_lang 5 | 6 | def initialize(args = {:filenames => []}) 7 | raise ArgumentError.new("No filenames given") unless args[:filenames] 8 | if args[:headers] 9 | 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) 10 | end 11 | 12 | @filenames = args[:filenames] 13 | 14 | @csv_filename = args[:csv_filename] || "translations.csv" 15 | @default_lang = args[:default_lang] 16 | @headers = args[:headers] || self.default_headers 17 | end 18 | 19 | def default_headers 20 | headers = ["Variables"] 21 | @filenames.each do |fname| 22 | headers << fname 23 | end 24 | headers 25 | end 26 | 27 | 28 | # Load all strings of a given file 29 | def load_strings(strings_filename) 30 | strings = ORDERED_HASH_CLASS.new 31 | File.open(strings_filename, 'r') do |strings_file| 32 | strings_file.read.each_line do |line| 33 | hash = self.parse_dotstrings_line(line) 34 | strings.merge!(hash) if hash 35 | end 36 | end 37 | strings 38 | end 39 | 40 | def parse_dotstrings_line(line) 41 | line.strip! 42 | if (line[0] != ?# and line[0] != ?=) 43 | m = line.match(/^[^\"]*\"(.+)\"[^=]+=[^\"]*\"(.*)\";/) 44 | return {m[1] => m[2]} unless m.nil? 45 | end 46 | end 47 | 48 | 49 | # Convert Localizable.strings files to one CSV file 50 | # output: strings hash has filename for keys and the content of csv 51 | def dotstrings_to_csv(write_to_file = true) 52 | # Parse .strings files 53 | strings = {} 54 | keys = nil 55 | lang_order = [] 56 | 57 | @filenames.each do |fname| 58 | header = fname 59 | strings[header] = load_strings(fname) 60 | keys ||= strings[header].keys 61 | end 62 | 63 | if(write_to_file) 64 | # Create csv file 65 | puts "Creating #{@csv_filename}" 66 | create_csv_file(keys, strings) 67 | else 68 | return keys, strings 69 | end 70 | end 71 | 72 | def basename(file_path) 73 | filename = File.basename(file_path) 74 | return filename.split('.')[0].to_sym if file_path 75 | end 76 | 77 | # Create the resulting file 78 | def create_csv_file(keys, strings) 79 | raise "csv_filename must not be nil" unless self.csv_filename 80 | CSVParserClass.open(self.csv_filename, "wb") do |csv| 81 | csv << @headers 82 | keys.each do |key| 83 | line = [key] 84 | default_val = strings[self.default_lang][key] if strings[self.default_lang] 85 | @filenames.each do |fname| 86 | lang = fname 87 | current_val = strings[lang][key] 88 | line << ((lang != self.default_lang and current_val == default_val) ? '' : current_val) 89 | end 90 | csv << line 91 | end 92 | puts "Done" 93 | end 94 | end 95 | 96 | end 97 | -------------------------------------------------------------------------------- /test/csvconverter/commands/test_command_csv2strings.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class TestCommand < Test::Unit::TestCase 3 | 4 | def test_csv2strings_with_multiple_2_languages 5 | options = { 6 | :filename => "test/data/test_data_multiple_langs.csv", 7 | :langs => {"English" => "en", "French" => "fr"} 8 | } 9 | CSV2StringsCommand.new([], options).csv2strings 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 | options = { 21 | :filename => "test/data/test_data_multiple_langs.csv", 22 | :langs => {"English" => "en", "French" => "fr"}, 23 | :default_path => "mynewlocation" 24 | } 25 | CSV2StringsCommand.new([], options).csv2strings 26 | 27 | # testing 28 | assert File.exist?("./mynewlocation/en.lproj/Localizable.strings"), "can't find output file for English" 29 | assert File.exist?("./mynewlocation/fr.lproj/Localizable.strings"), "can't find output file for French" 30 | 31 | #clean up 32 | system("rm -rf ./mynewlocation") 33 | end 34 | 35 | def test_csv2strings_with_fetch_google_doc 36 | omit if ENV['TRAVIS'] 37 | options = { 38 | :filename => "my_trads", 39 | :langs => {"English" => "en", "French" => "fr"}, 40 | :fetch => true 41 | } 42 | assert_nothing_raised do 43 | CSV2StringsCommand.new([], options).csv2strings 44 | end 45 | end 46 | 47 | def test_csv2strings_with_config_file 48 | system("cp .csvconverter.sample .csvconverter") 49 | 50 | assert_nothing_raised do 51 | CSV2StringsCommand.new.csv2strings 52 | end 53 | 54 | end 55 | 56 | def teardown 57 | 58 | system("rm -f .csvconverter") 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /test/csvconverter/commands/test_command_strings2csv.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class TestCommand < Test::Unit::TestCase 3 | 4 | def test_strings2csv 5 | options = {:filenames => ["test/data/test_data.strings"]} 6 | Strings2CSVCommand.new([], options).strings2csv 7 | 8 | assert File.exist?("translations.csv") 9 | 10 | #clean up 11 | system("rm -f translations.csv") 12 | end 13 | 14 | def test_strings2csv_with_dryrun_option 15 | options = {:filenames => ["test/data/test_data.strings"], :dryrun => true} 16 | Strings2CSVCommand.new([], options).strings2csv 17 | 18 | assert !File.exist?("translations.csv") 19 | 20 | #clean up 21 | system("rm -f translations.csv") 22 | end 23 | 24 | def test_strings2csv_with_output_file 25 | options = {:filenames => ["test/data/test_data.strings"], :csv_filename => "myfile.csv"} 26 | # -i, -o 27 | Strings2CSVCommand.new([], options).strings2csv 28 | 29 | assert File.exist?("myfile.csv") 30 | 31 | #clean up 32 | system("rm -f myfile.csv") 33 | end 34 | 35 | def test_strings2csv_with_headers 36 | options = {:filenames => ["test/data/test_data.strings"], :headers => ["constants", "english"]} 37 | # -i, -h 38 | Strings2CSVCommand.new([], options).strings2csv 39 | 40 | #TODO assertion or move test on at lib level 41 | 42 | #clean up 43 | system("rm -f translations.csv") 44 | end 45 | 46 | def test_strings2csv_with_two_files 47 | options = { 48 | :filenames => ["test/data/test_en.strings", "test/data/test_fr.strings"], 49 | :headers => %w{Constants English French}, 50 | :csv_filename => "enfr.csv" 51 | } 52 | # --filenames, --headers, -o 53 | Strings2CSVCommand.new([], options).strings2csv 54 | 55 | #TODO assertion 56 | 57 | #clean up 58 | system("rm -f enfr.csv") 59 | end 60 | 61 | def test_strings2csv_with_empty_lines 62 | options = { 63 | :filenames => ["test/data/test_with_nil.strings"], 64 | :csv_filename => "test_with_nil.csv" 65 | } 66 | # -i, -o 67 | 68 | 69 | assert_nothing_raised do 70 | Strings2CSVCommand.new([], options).strings2csv 71 | end 72 | assert system("diff test_with_nil.csv test/data/test_with_nil.csv"), "no difference on output" 73 | 74 | #clean up 75 | system("rm -f test_with_nil.csv") 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /test/csvconverter/test_bins.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class TestBins < Test::Unit::TestCase 3 | 4 | def test_csv2strings_with_google_doc 5 | omit if ENV['TRAVIS'] 6 | assert_nothing_raised do 7 | system("./bin/csv2strings --fetch --filename test.csv") 8 | end 9 | assert_equal $?.exitstatus, 0 10 | end 11 | 12 | def test_csv2strings_with_config_file 13 | system("cp .csvconverter.sample .csvconverter") 14 | 15 | assert_nothing_raised NameError do 16 | system("./bin/csv2strings") 17 | end 18 | assert_equal $?.exitstatus, 0 19 | end 20 | 21 | def teardown 22 | system("rm -f .csvconverter") 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/csvconverter/test_csv2strings.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class TestCSV2Strings < Test::Unit::TestCase 3 | 4 | def test_converting_csv_to_dotstrings 5 | csv_file = "test/data/test_data.csv" 6 | converter = CSV2Strings.new(csv_file, 'English' => [:en]) 7 | converter.csv_to_dotstrings 8 | assert File.exists?("en.lproj/Localizable.strings"), "the ouptut file does not exist" 9 | end 10 | 11 | def test_converting_csv_to_dotstrings_one_output_option 12 | csv_file = "test/data/test_data.csv" 13 | single_file = 'myApp.strings' 14 | converter = CSV2Strings.new(csv_file, 15 | {'English' => [:en]}, 16 | :output_file => single_file) 17 | converter.csv_to_dotstrings 18 | assert File.exists?(single_file), "the ouptut file does not exist" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/csvconverter/test_strings2csv.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | class TestStrings2CSV < Test::Unit::TestCase 3 | 4 | def test_parse_dotstrings_line_with_good_string 5 | input = String.new(<<-EOS) 6 | "MY_CONSTANT" = "This is ok"; 7 | EOS 8 | expected_output = {"MY_CONSTANT"=>"This is ok"} 9 | 10 | output = Strings2CSV.new.parse_dotstrings_line input 11 | assert_equal output, expected_output 12 | end 13 | 14 | def test_parse_dotstrings_line_with_single_quote 15 | input = String.new(<<-EOS) 16 | "MY_CONSTANT" = "This 'is' ok"; 17 | EOS 18 | expected_output = {"MY_CONSTANT"=>"This 'is' ok"} 19 | 20 | output = Strings2CSV.new.parse_dotstrings_line input 21 | assert_equal output, expected_output 22 | end 23 | 24 | def test_parse_dotstrings_line_with_double_quotes 25 | input = String.new(<<-EOS) 26 | "MY_CONSTANT" = "This "is" ok"; 27 | EOS 28 | expected_output = {"MY_CONSTANT"=>"This \"is\" ok"} 29 | 30 | output = Strings2CSV.new.parse_dotstrings_line input 31 | assert_equal output, expected_output 32 | end 33 | 34 | def test_parse_dotstrings_line_with_wrong_string 35 | input = String.new(<<-EOS) 36 | "MY_CONSTANT" = "wrong syntax" 37 | EOS 38 | 39 | output = Strings2CSV.new.parse_dotstrings_line input 40 | assert_nil output, "output should be nil with wrong syntax" 41 | end 42 | 43 | def test_load_strings_with_wrong_file 44 | assert_raise(Errno::ENOENT) do 45 | output = Strings2CSV.new.load_strings "file that does not exist.strings" 46 | end 47 | end 48 | 49 | def test_load_strings 50 | expected_output = {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"} 51 | output = Strings2CSV.new.load_strings "test/data/test_data.strings" 52 | assert_equal expected_output, output 53 | end 54 | 55 | def test_load_strings_with_empty_lines 56 | assert_nothing_raised do 57 | output = Strings2CSV.new.load_strings "test/data/test_with_nil.strings" 58 | end 59 | end 60 | 61 | def test_dotstrings_to_csv 62 | converter = Strings2CSV.new(:filenames => ["test/data/test_data.strings"]) 63 | keys, strings = converter.dotstrings_to_csv(false) 64 | assert_equal ["ERROR_HANDLER_WARNING_DISMISS", "ANOTHER_STRING"], keys 65 | expected_strings = {"test/data/test_data.strings" => {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"}} 66 | assert_equal expected_strings, strings 67 | end 68 | 69 | def test_create_csv_file 70 | keys = ["ERROR_HANDLER_WARNING_DISMISS", "ANOTHER_STRING"] 71 | filename = "test_data" 72 | strings = {filename => {"ERROR_HANDLER_WARNING_DISMISS" => "OK", "ANOTHER_STRING" => "hello"}} 73 | 74 | converter = Strings2CSV.new(:headers => %w{variables english}, :filenames => [filename]) 75 | 76 | converter.create_csv_file(keys, strings) 77 | assert File.exist?(converter.csv_filename) 78 | end 79 | 80 | def test_initialize 81 | csv_filename = "file.csv" 82 | filenames = %w{"french.strings english.strings"} 83 | headers = %w{"constants french english"} 84 | converter = Strings2CSV.new({ 85 | :csv_filename => csv_filename, 86 | :headers => headers, 87 | :filenames => filenames 88 | }) 89 | 90 | assert_equal csv_filename, converter.csv_filename 91 | assert_equal headers, converter.headers 92 | assert_equal filenames, converter.filenames 93 | end 94 | 95 | def test_initialize_with_default_values 96 | converter = Strings2CSV.new 97 | assert_not_nil converter.csv_filename 98 | end 99 | 100 | end 101 | -------------------------------------------------------------------------------- /test/data/test_data.csv: -------------------------------------------------------------------------------- 1 | variables,English 2 | ERROR_HANDLER_WARNING_DISMISS,OK 3 | ANOTHER_STRING,hello 4 | -------------------------------------------------------------------------------- /test/data/test_data.strings: -------------------------------------------------------------------------------- 1 | "ERROR_HANDLER_WARNING_DISMISS" = "OK"; 2 | "ANOTHER_STRING" = "hello"; -------------------------------------------------------------------------------- /test/data/test_data_multiple_langs.csv: -------------------------------------------------------------------------------- 1 | variables,English,German,French,Spanish 2 | GREETINGS,Hello,Hallo,Salut,Buenos dias 3 | ANOTHER_STRING,testEN,,, 4 | -------------------------------------------------------------------------------- /test/data/test_en.strings: -------------------------------------------------------------------------------- 1 | "GREETINGS" = "Hello"; 2 | "ANOTHER_STRING" = "testEN"; 3 | -------------------------------------------------------------------------------- /test/data/test_fr.strings: -------------------------------------------------------------------------------- 1 | "GREETINGS" = "Bonjour"; 2 | "ANOTHER_STRING" = "testFR"; 3 | -------------------------------------------------------------------------------- /test/data/test_with_nil.csv: -------------------------------------------------------------------------------- 1 | Variables,test/data/test_with_nil.strings 2 | GREETINGS,Hello 3 | ANOTHER_STRING,testEN 4 | -------------------------------------------------------------------------------- /test/data/test_with_nil.strings: -------------------------------------------------------------------------------- 1 | "GREETINGS" = "Hello"; 2 | 3 | "ANOTHER_STRING" = "testEN"; 4 | 5 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'simplecov' 3 | require 'coveralls' 4 | 5 | SimpleCov.formatter = Coveralls::SimpleCov::Formatter 6 | SimpleCov.start do 7 | add_filter '/test/' 8 | end 9 | rescue LoadError 10 | puts 'Coverage disabled, enable by installing simplecov' 11 | end 12 | 13 | require 'test/unit' 14 | 15 | require 'csvconverter' 16 | require "csvconverter/commands/strings2csv_command" 17 | require "csvconverter/commands/csv2strings_command" 18 | --------------------------------------------------------------------------------