ad_localize 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4ae0a65677bc70e3175aae752514334aac36642bfb2a2dc2ce8e750f70bbf1d2
4
+ data.tar.gz: 30fb7aa05e9b7b6d27f608ec8d9c712f71be21be91560f082cb13f935167e258
5
+ SHA512:
6
+ metadata.gz: f9e675d6438e1883632aa685e74166021fbe71e75ae1de4b4fda6d93b5c3705b177e0b19dc7399737261d97499c6521c5f746228e021df5401f97ef3fa57ccd6
7
+ data.tar.gz: 86b8d0efe4d91fe6eebb6849a76e1bb090c797de13ce3d430fb8a49efa7c93986626a1bb31bfccc639b19006ab7f6edae8c60e339f36b3aeb5d8734fc3e5b024
data/bin/ad_localize ADDED
@@ -0,0 +1,3 @@
1
+ #! /usr/bin/env ruby
2
+ require 'ad_localize'
3
+ AdLocalize.run
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+ require 'byebug'
6
+ require 'fileutils'
7
+ require 'pathname'
8
+ require 'yaml'
9
+ require 'logger'
10
+ require 'colorize'
11
+ require 'csv'
12
+ require 'active_support'
13
+ require 'open-uri'
14
+ require 'optparse'
15
+ require 'json'
16
+ require 'nokogiri'
17
+
18
+ require 'ad_localize/ad_logger'
19
+ require 'ad_localize/constant'
20
+ require 'ad_localize/csv_file_manager'
21
+ require 'ad_localize/csv_parser'
22
+ require 'ad_localize/option_handler'
23
+ require 'ad_localize/runner'
24
+ require 'ad_localize/platform/platform_formatter'
25
+ require 'ad_localize/platform/android_formatter'
26
+ require 'ad_localize/platform/ios_formatter'
27
+ require 'ad_localize/platform/json_formatter'
28
+ require 'ad_localize/platform/yml_formatter'
29
+
30
+ module AdLocalize
31
+ LOGGER = AdLogger.new
32
+
33
+ def self.run
34
+ Runner.new.run
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ module AdLocalize
2
+ class AdLogger
3
+ SEVERITY = { debug: Logger::DEBUG , info: Logger::INFO, warn: Logger::WARN, error: Logger::ERROR, fatal: Logger::FATAL, unknown: Logger::UNKNOWN }
4
+ AUTHORIZED_COLORS = [:black, :yellow, :red, :blue, :green]
5
+
6
+ def initialize
7
+ @logger = Logger.new(STDOUT)
8
+ @logger.level = Logger::INFO
9
+ end
10
+
11
+ def log(level, color, text)
12
+ exit unless text and level and color
13
+
14
+ level, color = [level, color].map(&:to_sym)
15
+ raise "Color not supported" unless AUTHORIZED_COLORS.include? color
16
+ raise "Invalid severity" unless SEVERITY.dig(level)
17
+
18
+ @logger.add(SEVERITY.dig(level), text.send(color))
19
+ end
20
+
21
+ def debug!
22
+ @logger.level = Logger::DEBUG
23
+ end
24
+
25
+ def debug?
26
+ @logger.debug?
27
+ end
28
+
29
+ def close
30
+ @logger.close
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ module AdLocalize
2
+ module Constant
3
+ SUPPORTED_PLATFORMS = %w(ios android yml json)
4
+ PLURAL_KEY_SYMBOL = :plural
5
+ SINGULAR_KEY_SYMBOL = :singular
6
+ COMMENT_KEY_SYMBOL = :comment
7
+ COMMENT_KEY_COLUMN_IDENTIFIER = "comment"
8
+ DEFAULT_CONFIG = {
9
+ platforms: {
10
+ export_directory_names: {
11
+ ios: "%{locale}.lproj",
12
+ android: "values%{locale}"
13
+ }
14
+ }
15
+ }
16
+ CONFIG = YAML.load_file(Pathname::pwd + 'config.yml').deep_symbolize_keys rescue DEFAULT_CONFIG
17
+ EXPORT_FOLDER = 'exports'
18
+ IOS_SINGULAR_EXPORT_FILENAME = "Localizable.strings"
19
+ IOS_PLURAL_EXPORT_FILENAME = "Localizable.stringsdict"
20
+ ANDROID_EXPORT_FILENAME = "strings.xml"
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ module AdLocalize
2
+ class CsvFileManager
3
+ CSV_CONTENT_TYPES = %w(text/csv text/plain)
4
+
5
+ class << self
6
+ def csv?(file)
7
+ !file.nil? && CSV_CONTENT_TYPES.include?(`file --brief --mime-type '#{file}'`.strip)
8
+ end
9
+
10
+ # Returns the downloaded file name (it is located in the current directory)
11
+ def download_from_drive(key)
12
+ LOGGER.log(:info, :black, "Downloading file from google drive...")
13
+ download_string_path = "./#{key}.csv"
14
+ begin
15
+ File.open(download_string_path, "wb") do |saved_file|
16
+ # the following "open" is provided by open-uri
17
+ open(drive_download_url(key), "rb") do |read_file|
18
+ saved_file.write(read_file.read)
19
+ end
20
+ File.basename saved_file
21
+ end
22
+ rescue => e
23
+ delete_drive_file(download_string_path)
24
+ LOGGER.log(:error, :red, e.message)
25
+ exit
26
+ end
27
+ end
28
+
29
+ def select_csvs(files)
30
+ files.select do |file|
31
+ LOGGER.log(:error, :red, "#{file} is not a csv. It will be ignored") unless self.csv?(file)
32
+ self.csv?(file)
33
+ end
34
+ end
35
+
36
+ def delete_drive_file(file)
37
+ Pathname.new(file).delete unless file.nil?
38
+ end
39
+
40
+ private
41
+ def drive_download_url(key)
42
+ "https://docs.google.com/spreadsheets/d/#{key}/export?format=csv&id=#{key}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,127 @@
1
+ module AdLocalize
2
+ class CsvParser
3
+ CSV_WORDING_KEYS_COLUMN = "key"
4
+ PLURAL_KEY_REGEXP = /\#\#\{(\w+)\}/
5
+
6
+ attr_accessor :locales
7
+
8
+ def initialize
9
+ @locales = []
10
+ end
11
+
12
+ # Raise a StandardError if no locale is detected in the csv
13
+ def extract_data(file_name)
14
+ data = {}
15
+ CSV.foreach(file_name, headers: true, skip_blanks: true) do |row|
16
+ validity_status = check_row(row)
17
+ if validity_status.zero?
18
+ next
19
+ elsif validity_status == -1
20
+ exit
21
+ else
22
+ find_locales(row) if locales.empty?
23
+ row_data = parse_row(row)
24
+ data.deep_merge!(row_data) unless row_data.nil?
25
+ end
26
+ end
27
+ data
28
+ end
29
+
30
+ private
31
+ # Returns 1 if row is ok, 0 if row there are missing information and -1 if row is not a csv row
32
+ def check_row(row)
33
+ valid_row = 1
34
+ # Check non empty row
35
+ if row.field(CSV_WORDING_KEYS_COLUMN).nil?
36
+ LOGGER.log(:error, :red, "Missing key in line #{$.}") unless row.fields.all?(&:nil?)
37
+ valid_row = 0
38
+ elsif not row.headers.include?(CSV_WORDING_KEYS_COLUMN)
39
+ LOGGER.log(:error, :red, "[CSV FORMAT] #{file_name} is not a valid file")
40
+ valid_row = -1
41
+ end
42
+ return valid_row
43
+ end
44
+
45
+ def find_locales(row)
46
+ wording_key_column_index = row.index(CSV_WORDING_KEYS_COLUMN)
47
+ if row.length > wording_key_column_index
48
+ self.locales = row.headers.slice((wording_key_column_index+1)..-1).compact.reject do |header|
49
+ header.to_s.include? Constant::COMMENT_KEY_COLUMN_IDENTIFIER
50
+ end
51
+ end
52
+ if locales.empty?
53
+ raise 'NO DETECTED LOCALES'
54
+ else
55
+ LOGGER.log(:debug, :black, "DETECTED LOCALES : #{locales.join(', ')}")
56
+ end
57
+ end
58
+
59
+ def parse_row(row)
60
+ key_infos = parse_key(row)
61
+ return nil if key_infos.nil?
62
+
63
+ locales.each_with_object({ key_infos.dig(:key) => {} }) do |locale, output|
64
+ current_key = key_infos.dig(:key)
65
+ current_key_already_has_wording_for_locale = output[current_key].key? locale.to_sym
66
+ output[current_key][locale.to_sym] = { key_infos.dig(:numeral_key) => {} } unless current_key_already_has_wording_for_locale
67
+
68
+ if key_infos.dig(:numeral_key) == Constant::SINGULAR_KEY_SYMBOL
69
+ trace_wording(row[locale], "[#{locale.upcase}] Singular key ---> #{current_key}", "[#{locale.upcase}] Missing translation for #{current_key}")
70
+ value = row[locale] || default_wording(locale, current_key)
71
+ elsif key_infos.dig(:numeral_key) == Constant::PLURAL_KEY_SYMBOL
72
+ trace_wording(row[locale],
73
+ "[#{locale.upcase}] Plural key ---> plural_identifier : #{key_infos.dig(:plural_identifier)}, key : #{current_key}",
74
+ "[#{locale.upcase}] Missing translation for #{current_key} (#{key_infos.dig(:plural_identifier)})")
75
+ value = { key_infos.dig(:plural_identifier) => row[locale] || default_wording(locale, "#{current_key} (#{key_infos.dig(:plural_identifier)})") }
76
+ else
77
+ raise "Unknown numeral key #{key_infos.dig(:numeral_key)}"
78
+ end
79
+
80
+ check_wording_parameters(row[locale], locale, current_key)
81
+ output[current_key][locale.to_sym][key_infos.dig(:numeral_key)] = value
82
+ locale_comment_column = "#{Constant::COMMENT_KEY_COLUMN_IDENTIFIER} #{locale}"
83
+ output[current_key][locale.to_sym][Constant::COMMENT_KEY_SYMBOL] = row[locale_comment_column] unless locale_comment_column.blank? and row[locale_comment_column].blank?
84
+ end
85
+ end
86
+
87
+ def parse_key(row)
88
+ key = row.field(CSV_WORDING_KEYS_COLUMN)
89
+ plural_prefix = key.match(PLURAL_KEY_REGEXP)
90
+ plural_identifier = nil
91
+ invalid_plural = false
92
+
93
+ if plural_prefix.nil?
94
+ numeral_key = Constant::SINGULAR_KEY_SYMBOL
95
+ else
96
+ numeral_key = Constant::PLURAL_KEY_SYMBOL
97
+ key = plural_prefix.pre_match
98
+ plural_identifier = plural_prefix.captures&.first
99
+ LOGGER.log(:debug, :red, "Invalid key #{key}") if key.nil?
100
+ LOGGER.log(:debug, :red, "Empty plural prefix!") if plural_identifier.nil?
101
+ invalid_plural = plural_identifier.nil?
102
+ end
103
+
104
+ (key.nil? or invalid_plural) ? nil : { key: key.to_sym, numeral_key: numeral_key, plural_identifier: plural_identifier&.to_sym }
105
+ end
106
+
107
+ def default_wording(locale, key)
108
+ LOGGER.debug? ? "<[#{locale.upcase}] Missing translation for #{key}>" : ""
109
+ end
110
+
111
+ def trace_wording(wording, present_message, missing_message)
112
+ if wording
113
+ LOGGER.log(:debug, :black, present_message)
114
+ else
115
+ LOGGER.log(:debug, :yellow, missing_message)
116
+ end
117
+ end
118
+
119
+ def check_wording_parameters(value, locale, key)
120
+ formatted_arguments = value&.scan(/%(\d$)?@/) || []
121
+ if formatted_arguments.size >= 2
122
+ is_all_ordered = formatted_arguments.inject(true){ |is_ordered, match| is_ordered &&= !match.join.empty? }
123
+ LOGGER.log(:warn, :red, "[#{locale.upcase}] Multiple arguments for #{key} but no order") unless is_all_ordered
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,57 @@
1
+ module AdLocalize
2
+ class OptionHandler
3
+ GOOGLE_DRIVE_DOCUMENT_ID = { length: 32, regexp: /\A[\w-]+\Z/ }
4
+
5
+ class << self
6
+ def parse
7
+ args = { debug: false, only: nil}
8
+
9
+ opt_parser = OptionParser.new do |opts|
10
+ opts.banner = "Usage: ruby bin/export [options] file(s)"
11
+
12
+
13
+ opts.on("-h", "--help", "Prints help") do
14
+ puts opts
15
+ exit
16
+ end
17
+ opts.on("-d", "--debug", "Run in debug mode") do
18
+ args[:debug] = true
19
+ LOGGER.debug!
20
+ end
21
+ opts.on("-k", "--drive-key #{GOOGLE_DRIVE_DOCUMENT_ID.dig(:length)}_characters", String, "Use google drive spreadsheets") do |key|
22
+ is_valid_drive_key = !!(key =~ GOOGLE_DRIVE_DOCUMENT_ID.dig(:regexp)) && (key.size >= GOOGLE_DRIVE_DOCUMENT_ID.dig(:length))
23
+ args[:drive_key] = is_valid_drive_key ? key : nil
24
+ end
25
+ opts.on("-o", "--only platform1,platform2", Array, "Only generate localisation files for the specified platforms. Supported platforms : #{Constant::SUPPORTED_PLATFORMS.join(', ')}") do |platforms|
26
+ args[:only] = filter_option_args("-o", platforms) { |platform| !!Constant::SUPPORTED_PLATFORMS.index(platform) }
27
+ end
28
+ opts.on("-t", "--target-dir PATH", String, "Path to the target directory") do |output_path|
29
+ pn = Pathname.new(output_path)
30
+ if pn.directory? and pn.readable? and pn.writable?
31
+ args[:output_path] = output_path
32
+ else
33
+ raise ArgumentError.new("Invalid target directory. Check the permissions")
34
+ end
35
+ end
36
+ end
37
+
38
+ begin
39
+ opt_parser.parse!
40
+ rescue OptionParser::MissingArgument => e
41
+ LOGGER.log(:error, :red, "Missing argument for option #{e.args.join(',')}")
42
+ rescue ArgumentError => e
43
+ LOGGER.log(:error, :red, e.message)
44
+ end
45
+ args
46
+ end
47
+
48
+ private
49
+ def filter_option_args(option, args)
50
+ result = args.select { |arg| yield(arg) }
51
+ invalid_args = args - result
52
+ LOGGER.log(:debug, :red, "Invalid arg(s) for option #{option}: #{invalid_args.join(', ')}") unless invalid_args.empty?
53
+ result
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,68 @@
1
+ module AdLocalize::Platform
2
+ class AndroidFormatter < PlatformFormatter
3
+ def platform
4
+ :android
5
+ end
6
+
7
+ def export(locale, data, export_extension = nil, substitution_format = nil)
8
+ locale = locale.to_sym
9
+ export_dir_suffix = (locale == default_locale) ? "" : "-#{locale.downcase}"
10
+ create_locale_dir(export_dir_suffix)
11
+
12
+ xml_doc = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
13
+ xml.resources {
14
+ data.each do |key, wording|
15
+ singular_wording = wording.dig(locale, AdLocalize::Constant::SINGULAR_KEY_SYMBOL)
16
+ add_singular_wording_to_xml(key, singular_wording, xml) unless singular_wording.blank?
17
+
18
+ plural_wording = wording.dig(locale, AdLocalize::Constant::PLURAL_KEY_SYMBOL)
19
+ add_plural_wording_to_xml(key, plural_wording, xml) unless plural_wording.blank?
20
+ end
21
+ }
22
+ end
23
+
24
+ export_dir(export_dir_suffix).join(AdLocalize::Constant::ANDROID_EXPORT_FILENAME).open("w") do |file|
25
+ file.puts xml_doc.to_xml(indent: 4)
26
+ end
27
+ AdLocalize::LOGGER.log(:debug, :black, "Android [#{locale}] ---> DONE!")
28
+ end
29
+
30
+ private
31
+ def ios_converter(value)
32
+ processedValue = value.gsub(/</, "&lt;")
33
+ processedValue = processedValue.gsub(/>/, "&gt;")
34
+ processedValue = processedValue.gsub(/(?<!\\)'/, "\\\\'")
35
+ processedValue = processedValue.gsub(/(?<!\\)\"/, "\\\"")
36
+ processedValue = processedValue.gsub(/&(?!(?:amp|lt|gt|quot|apos);)/, '&amp;')
37
+ processedValue = processedValue.gsub(/(%(\d+\$)?@)/, '%\2s') # should match values like %1$s and %s
38
+ processedValue = processedValue.gsub(/(%((\d+\$)?(\d+)?)i)/, '%\2d') # should match values like %i, %3$i, %01i, %1$02i
39
+ processedValue = processedValue.gsub(/%(?!((\d+\$)?(s|(\d+)?d)))/, '%%') # negative lookahead: identifies when user really wants to display a %
40
+ processedValue = processedValue.gsub("\\U", "\\u")
41
+ "\"#{processedValue}\""
42
+ end
43
+
44
+ def add_singular_wording_to_xml(key, text, xml)
45
+ xml.string(name: key) {
46
+ add_wording_text_to_xml(text, xml)
47
+ }
48
+ end
49
+
50
+ def add_plural_wording_to_xml(key, plural_hash, xml)
51
+ xml.plurals(name: key) {
52
+ plural_hash.each do |plural_type, plural_text|
53
+ next if plural_text.blank?
54
+ xml.item(quantity: plural_type) {
55
+ add_wording_text_to_xml(plural_text, xml)
56
+ }
57
+ end
58
+ }
59
+ end
60
+
61
+ def add_wording_text_to_xml(wording_text, xml)
62
+ raise 'Wording text should not be empty' if wording_text.blank?
63
+ xml.text(
64
+ ios_converter(wording_text)
65
+ )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,69 @@
1
+ module AdLocalize::Platform
2
+ class IosFormatter < PlatformFormatter
3
+ def platform
4
+ :ios
5
+ end
6
+
7
+ def export(locale, data, export_extension = nil, substitution_format = nil)
8
+ create_locale_dir(locale)
9
+ [AdLocalize::Constant::PLURAL_KEY_SYMBOL, AdLocalize::Constant::SINGULAR_KEY_SYMBOL].each do |numeral_key|
10
+ numeral_data = data.select {|key, wording| wording.dig(locale.to_sym)&.key? numeral_key}
11
+ if numeral_data.empty?
12
+ AdLocalize::LOGGER.log(:info, :black, "[#{locale.upcase}] no #{numeral_key.to_s} keys were found to generate the file")
13
+ else
14
+ send("write_#{numeral_key}", locale, numeral_data)
15
+ end
16
+ end
17
+ end
18
+
19
+ protected
20
+ def write_singular(locale, singulars)
21
+ locale = locale.to_sym
22
+
23
+ singulars.each do |key, locales_wording|
24
+ value = locales_wording.dig(locale, AdLocalize::Constant::SINGULAR_KEY_SYMBOL)
25
+ comment = locales_wording.dig(locale, AdLocalize::Constant::COMMENT_KEY_SYMBOL)
26
+ export_dir(locale).join(AdLocalize::Constant::IOS_SINGULAR_EXPORT_FILENAME).open("a") do |file|
27
+ line = "\"#{key}\" = \"#{value}\";"
28
+ line << " // #{comment}" unless comment.nil?
29
+ line << "\n"
30
+ file.puts line
31
+ end
32
+ end
33
+ AdLocalize::LOGGER.log(:debug, :black, "iOS singular [#{locale}] ---> DONE!")
34
+ end
35
+
36
+ def write_plural(locale, plurals)
37
+ locale = locale.to_sym
38
+
39
+ xml_doc = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
40
+ xml.plist {
41
+ xml.dict {
42
+ plurals.each do |wording_key, translations|
43
+ xml.key wording_key
44
+ xml.dict {
45
+ xml.key "NSStringLocalizedFormatKey"
46
+ xml.string "%\#@key@"
47
+ xml.key "key"
48
+ xml.dict {
49
+ xml.key "NSStringFormatSpecTypeKey"
50
+ xml.string "NSStringPluralRuleType"
51
+ xml.key "NSStringFormatValueTypeKey"
52
+ xml.string "d"
53
+ translations[locale][AdLocalize::Constant::PLURAL_KEY_SYMBOL].each do |wording_type, wording_value|
54
+ xml.key wording_type
55
+ xml.string wording_value
56
+ end
57
+ }
58
+ }
59
+ end
60
+ }
61
+ }
62
+ end
63
+ export_dir(locale).join(AdLocalize::Constant::IOS_PLURAL_EXPORT_FILENAME).open("w") do |file|
64
+ file.puts xml_doc.to_xml(indent: 4)
65
+ end
66
+ AdLocalize::LOGGER.log(:debug, :black, "iOS plural [#{locale}] ---> DONE!")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,18 @@
1
+ module AdLocalize::Platform
2
+ class JsonFormatter < PlatformFormatter
3
+ def platform
4
+ :json
5
+ end
6
+
7
+ def export(locale, data, export_extension = "json", substitution_format = "angular")
8
+ super(locale, data, export_extension, substitution_format) do |json_data, file|
9
+ file.puts json_data.to_json
10
+ end
11
+ end
12
+
13
+ protected
14
+ def ios_converter(value)
15
+ value.gsub(/(%(?!)?(\d+\$)?[@isd])/, "VARIABLE_TO_SET")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,76 @@
1
+ module AdLocalize::Platform
2
+ class PlatformFormatter
3
+ PLURAL_KEY_SYMBOL = :plural
4
+ SINGULAR_KEY_SYMBOL = :singular
5
+ OPTIONS = {output_path: ".."}
6
+
7
+ attr_accessor :platform_dir
8
+ attr_reader :default_locale, :output_path
9
+
10
+ def initialize(default_locale, output_path)
11
+ @default_locale = default_locale.to_sym
12
+ @output_path = output_path # Must be set before platform_dir
13
+ @platform_dir = export_base_directory.join(platform.to_s)
14
+ create_platform_dir
15
+ end
16
+
17
+ def platform
18
+ raise 'Please override me!'
19
+ end
20
+
21
+ def export(locale, data, export_extension, substitution_format)
22
+ locale = locale.to_sym
23
+ formatted_data = data.each_with_object({}) do |(key, wording), hash_acc|
24
+ hash_acc[locale.to_s] = {} unless hash_acc.key? locale.to_s
25
+ if wording.dig(locale)&.key? :singular
26
+ value = ios_converter(wording.dig(locale, :singular))
27
+ hash_acc[locale.to_s][key.to_s] = value
28
+ end
29
+ if wording.dig(locale)&.key? :plural
30
+ hash_acc[locale.to_s][key.to_s] = {} unless hash_acc[locale.to_s].key? key.to_s
31
+ wording.dig(locale, :plural).each do |plural_type, plural_text|
32
+ value = ios_converter(plural_text)
33
+ hash_acc[locale.to_s][key.to_s][plural_type.to_s] = value
34
+ end
35
+ end
36
+ end
37
+
38
+ platform_dir.join("#{locale.to_s}.#{export_extension}").open("w") do |file|
39
+ yield(formatted_data, file)
40
+ end
41
+
42
+ AdLocalize::LOGGER.log(:debug, :black, "#{platform.to_s.upcase} [#{locale}] ---> DONE!")
43
+ end
44
+
45
+ protected
46
+ def export_base_directory
47
+ if output_path
48
+ Pathname.new(output_path)
49
+ else
50
+ Pathname::pwd.join(AdLocalize::Constant::EXPORT_FOLDER)
51
+ end
52
+ end
53
+
54
+ def create_platform_dir
55
+ if platform_dir.directory?
56
+ FileUtils.rm_rf("#{platform_dir}/.", secure: true)
57
+ else
58
+ platform_dir.mkpath
59
+ end
60
+ end
61
+
62
+ def export_dir(locale)
63
+ platform_dir + AdLocalize::Constant::CONFIG.dig(:platforms, :export_directory_names, platform.to_sym) % { locale: locale.downcase }
64
+ end
65
+
66
+ def create_locale_dir(locale)
67
+ raise 'Locale needed' unless locale
68
+
69
+ if export_dir(locale).directory?
70
+ FileUtils.rm_rf("#{export_dir(locale)}/.", secure: true)
71
+ else
72
+ export_dir(locale).mkdir
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,18 @@
1
+ module AdLocalize::Platform
2
+ class YmlFormatter < PlatformFormatter
3
+ def platform
4
+ :yml
5
+ end
6
+
7
+ def export(locale, data, export_extension = "yml", substitution_format = "yml")
8
+ super(locale, data, export_extension, substitution_format) do |yml_data, file|
9
+ file.puts yml_data.to_yaml
10
+ end
11
+ end
12
+
13
+ protected
14
+ def ios_converter(value)
15
+ value.gsub(/(%(?!)?(\d+\$)?[@isd])/, "VARIABLE_TO_SET")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module AdLocalize
2
+ class Runner
3
+ attr_accessor :options
4
+
5
+ def initialize
6
+ @options = OptionHandler.parse
7
+ end
8
+
9
+ def run(args = ARGV)
10
+ LOGGER.log(:info, :black, "OPTIONS : #{options}")
11
+ input_files = (args + [options.dig(:drive_key)]).compact # drive_key can be nil
12
+ if input_files.length.zero?
13
+ LOGGER.log(:error, :red, "No CSV to parse. Use option -h to see how to use this script")
14
+ else
15
+ file_to_parse = args.first
16
+ LOGGER.log(:warn, :yellow, "Only one CSV can be treated - the priority goes to #{file_to_parse}") if input_files.length > 1
17
+ if args.empty?
18
+ options[:drive_file] = CsvFileManager.download_from_drive(options.dig(:drive_key))
19
+ file_to_parse = options.dig(:drive_file)
20
+ end
21
+ CsvFileManager.csv?(file_to_parse) ? export(file_to_parse) : LOGGER.log(:error, :red, "#{file_to_parse} is not a csv")
22
+ CsvFileManager.delete_drive_file(options[:drive_file]) if options[:drive_file]
23
+ end
24
+ end
25
+
26
+ private
27
+ def export(file)
28
+ LOGGER.log(:info, :black, "********* PARSING #{file} *********")
29
+ LOGGER.log(:info, :black, "Extracting data from file...")
30
+ parser = CsvParser.new
31
+ data = parser.extract_data(file)
32
+ if data.empty?
33
+ LOGGER.log(:error, :red, "No data were found in the file - cannot start the file generation process")
34
+ else
35
+ export_platforms = options.dig(:only) || Constant::SUPPORTED_PLATFORMS
36
+ export_platforms.each do |platform|
37
+ platform_formatter = "AdLocalize::Platform::#{platform.to_s.camelize}Formatter".constantize.new(parser.locales.first, options.dig(:output_path))
38
+ parser.locales.each do |locale|
39
+ platform_formatter.export(locale, data)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ad_localize
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Edouard Siegel
8
+ - Patrick Nollet
9
+ - Adrien Couque
10
+ - Tarek Belkahia
11
+ - Hugo Hache
12
+ - Vincent Brison
13
+ - Joanna Vigné
14
+ - Nicolas Braun
15
+ - Corentin Huard
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+ date: 2018-04-18 00:00:00.000000000 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.16'
28
+ type: :development
29
+ prerelease: false
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.16'
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ - !ruby/object:Gem::Dependency
50
+ name: minitest
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '5.0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '5.0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: byebug
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '10.0'
77
+ - !ruby/object:Gem::Dependency
78
+ name: json
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.8'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 1.8.3
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '1.8'
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 1.8.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: nokogiri
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.8'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 1.8.2
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '1.8'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 1.8.2
117
+ - !ruby/object:Gem::Dependency
118
+ name: activesupport
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '5.1'
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 5.1.5
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '5.1'
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 5.1.5
137
+ - !ruby/object:Gem::Dependency
138
+ name: colorize
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: 0.8.1
144
+ type: :runtime
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: 0.8.1
151
+ description: " Convert a wording file in localization files. Supported export formats
152
+ are : iOS, Android,\n JSON and YAML "
153
+ email:
154
+ - joanna.vigne@fabernovel.com
155
+ - hugo.hache@fabernovel.com
156
+ - edouard.siegel@fabernovel.com
157
+ executables:
158
+ - ad_localize
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - bin/ad_localize
163
+ - lib/ad_localize.rb
164
+ - lib/ad_localize/ad_logger.rb
165
+ - lib/ad_localize/constant.rb
166
+ - lib/ad_localize/csv_file_manager.rb
167
+ - lib/ad_localize/csv_parser.rb
168
+ - lib/ad_localize/option_handler.rb
169
+ - lib/ad_localize/platform/android_formatter.rb
170
+ - lib/ad_localize/platform/ios_formatter.rb
171
+ - lib/ad_localize/platform/json_formatter.rb
172
+ - lib/ad_localize/platform/platform_formatter.rb
173
+ - lib/ad_localize/platform/yml_formatter.rb
174
+ - lib/ad_localize/runner.rb
175
+ homepage: https://technologies.fabernovel.com
176
+ licenses:
177
+ - MIT
178
+ metadata: {}
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubyforge_project:
195
+ rubygems_version: 2.7.3
196
+ signing_key:
197
+ specification_version: 4
198
+ summary: AdLocalize
199
+ test_files: []