ad_localize 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: []