ad_localize 4.1.1 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +6 -3
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +319 -0
- data/CHANGELOG.md +70 -15
- data/Gemfile +1 -0
- data/Gemfile.lock +63 -61
- data/README.md +37 -10
- data/Rakefile +1 -0
- data/ad_localize.gemspec +14 -13
- data/bin/console +1 -1
- data/exe/ad_localize +2 -1
- data/lib/ad_localize/ad_logger.rb +5 -10
- data/lib/ad_localize/cli.rb +10 -3
- data/lib/ad_localize/entities/key.rb +3 -76
- data/lib/ad_localize/entities/locale_wording.rb +32 -52
- data/lib/ad_localize/entities/platform.rb +13 -0
- data/lib/ad_localize/entities/simple_wording.rb +6 -0
- data/lib/ad_localize/entities/wording_type.rb +11 -0
- data/lib/ad_localize/interactors/base_generate_files.rb +37 -0
- data/lib/ad_localize/interactors/download_spreadsheets.rb +20 -0
- data/lib/ad_localize/interactors/export_wording.rb +27 -18
- data/lib/ad_localize/interactors/generate_info_plist.rb +24 -0
- data/lib/ad_localize/interactors/generate_ios_files.rb +12 -0
- data/lib/ad_localize/interactors/generate_json.rb +20 -0
- data/lib/ad_localize/interactors/generate_localizable_strings.rb +24 -0
- data/lib/ad_localize/interactors/generate_localizable_strings_dict.rb +24 -0
- data/lib/ad_localize/interactors/generate_properties.rb +20 -0
- data/lib/ad_localize/interactors/generate_strings.rb +25 -0
- data/lib/ad_localize/interactors/generate_yaml.rb +20 -0
- data/lib/ad_localize/interactors/merge_wordings.rb +49 -18
- data/lib/ad_localize/interactors/parse_csv_files.rb +22 -0
- data/lib/ad_localize/interactors/process_export_request.rb +21 -0
- data/lib/ad_localize/mappers/locale_wording_to_hash.rb +45 -24
- data/lib/ad_localize/mappers/options_to_export_request.rb +14 -21
- data/lib/ad_localize/option_handler.rb +50 -26
- data/lib/ad_localize/parsers/csv_parser.rb +84 -0
- data/lib/ad_localize/parsers/key_parser.rb +62 -0
- data/lib/ad_localize/repositories/drive_repository.rb +53 -0
- data/lib/ad_localize/repositories/file_system_repository.rb +2 -1
- data/lib/ad_localize/requests/export_request.rb +97 -53
- data/lib/ad_localize/sanitizers/ios_sanitizer.rb +12 -0
- data/lib/ad_localize/{mappers/android_translation_mapper.rb → sanitizers/ios_to_android_sanitizer.rb} +8 -8
- data/lib/ad_localize/sanitizers/pass_through_sanitizer.rb +10 -0
- data/lib/ad_localize/serializers/info_plist_serializer.rb +9 -11
- data/lib/ad_localize/serializers/json_serializer.rb +3 -5
- data/lib/ad_localize/serializers/localizable_strings_serializer.rb +9 -11
- data/lib/ad_localize/serializers/localizable_stringsdict_serializer.rb +12 -19
- data/lib/ad_localize/serializers/properties_serializer.rb +9 -11
- data/lib/ad_localize/serializers/strings_serializer.rb +12 -21
- data/lib/ad_localize/serializers/templated_serializer.rb +51 -0
- data/lib/ad_localize/serializers/yaml_serializer.rb +4 -6
- data/lib/ad_localize/templates/android/strings.xml.erb +6 -6
- data/lib/ad_localize/templates/ios/Localizable.stringsdict.erb +14 -14
- data/lib/ad_localize/version.rb +2 -1
- data/lib/ad_localize/view_models/compound_wording_view_model.rb +2 -0
- data/lib/ad_localize/view_models/simple_wording_view_model.rb +2 -0
- data/lib/ad_localize.rb +33 -34
- metadata +86 -69
- data/lib/ad_localize/constant.rb +0 -6
- data/lib/ad_localize/entities/translation.rb +0 -32
- data/lib/ad_localize/entities/wording.rb +0 -24
- data/lib/ad_localize/interactors/execute_export_request.rb +0 -43
- data/lib/ad_localize/interactors/export_csv_files.rb +0 -17
- data/lib/ad_localize/interactors/export_g_spreadsheet.rb +0 -59
- data/lib/ad_localize/interactors/platforms/export_android_locale_wording.rb +0 -43
- data/lib/ad_localize/interactors/platforms/export_csv_locale_wording.rb +0 -25
- data/lib/ad_localize/interactors/platforms/export_ios_locale_wording.rb +0 -66
- data/lib/ad_localize/interactors/platforms/export_json_locale_wording.rb +0 -27
- data/lib/ad_localize/interactors/platforms/export_platform_factory.rb +0 -50
- data/lib/ad_localize/interactors/platforms/export_properties_locale_wording.rb +0 -33
- data/lib/ad_localize/interactors/platforms/export_yaml_locale_wording.rb +0 -27
- data/lib/ad_localize/mappers/csv_path_to_wording.rb +0 -73
- data/lib/ad_localize/mappers/ios_translation_mapper.rb +0 -12
- data/lib/ad_localize/mappers/translation_group_mapper.rb +0 -14
- data/lib/ad_localize/mappers/translation_mapper.rb +0 -30
- data/lib/ad_localize/mappers/value_range_to_wording.rb +0 -67
- data/lib/ad_localize/repositories/g_sheets_repository.rb +0 -44
- data/lib/ad_localize/requests/g_spreadsheet_options.rb +0 -47
- data/lib/ad_localize/requests/merge_policy.rb +0 -28
- data/lib/ad_localize/serializers/with_template.rb +0 -19
- data/lib/ad_localize/validators/key_validator.rb +0 -31
- data/lib/ad_localize/view_models/translation_group_view_model.rb +0 -19
- data/lib/ad_localize/view_models/translation_view_model.rb +0 -23
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AdLocalize
|
3
|
+
module Parsers
|
4
|
+
class CSVParser
|
5
|
+
COMMENT_KEY_COLUMN_IDENTIFIER = 'comment'.freeze
|
6
|
+
CSV_WORDING_KEYS_COLUMN = 'key'.freeze
|
7
|
+
|
8
|
+
def initialize(key_parser: nil)
|
9
|
+
@key_parser = key_parser.presence || KeyParser.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(csv_path:, export_request:)
|
13
|
+
locales = find_locales(csv_path: csv_path, export_request: export_request)
|
14
|
+
LOGGER.debug("#{csv_path} - locales : #{locales.to_sentence}")
|
15
|
+
return if locales.blank?
|
16
|
+
|
17
|
+
keys = find_keys(csv_path: csv_path)
|
18
|
+
wording = build_wording(
|
19
|
+
csv_path: csv_path,
|
20
|
+
locales: locales,
|
21
|
+
keys: keys,
|
22
|
+
export_request: export_request
|
23
|
+
)
|
24
|
+
wording
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def find_locales(csv_path:, export_request:)
|
30
|
+
csv = CSV.open(csv_path, headers: true, skip_blanks: true)
|
31
|
+
headers = csv.first.headers
|
32
|
+
locales = headers[headers.index(CSV_WORDING_KEYS_COLUMN).succ..-1].compact.reject do |header|
|
33
|
+
header.include?(COMMENT_KEY_COLUMN_IDENTIFIER)
|
34
|
+
end
|
35
|
+
export_request.locales.empty? ? locales : locales & export_request.locales
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_keys(csv_path:)
|
39
|
+
keys = {}
|
40
|
+
CSV.foreach(csv_path, headers: true, skip_blanks: true, skip_lines: /^#/) do |row|
|
41
|
+
next if row[CSV_WORDING_KEYS_COLUMN].blank?
|
42
|
+
|
43
|
+
raw_key = row[CSV_WORDING_KEYS_COLUMN].strip
|
44
|
+
key = @key_parser.call(raw_key: raw_key)
|
45
|
+
|
46
|
+
existing_key = keys.values.detect do |k|
|
47
|
+
k.id == key.id || (k.label == key.label && (k.variant_name.nil? || key.variant_name.nil?))
|
48
|
+
end
|
49
|
+
if existing_key
|
50
|
+
LOGGER.warn "A #{existing_key.type} value already exist for key '#{existing_key.label}'. Will skip new #{key.type} value. Remove duplicates."
|
51
|
+
else
|
52
|
+
keys[raw_key] = key
|
53
|
+
end
|
54
|
+
end
|
55
|
+
keys
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_wording(csv_path:, locales:, keys:, export_request:)
|
59
|
+
default_locale = locales.first
|
60
|
+
wording = Hash.new { |hash, key|
|
61
|
+
hash[key] = Entities::LocaleWording.new(locale: key, is_default: key == default_locale)
|
62
|
+
}
|
63
|
+
added_keys = Hash.new { |hash, key| hash[key] = false }
|
64
|
+
CSV.foreach(csv_path, headers: true, skip_blanks: true, skip_lines: /^#/) do |row|
|
65
|
+
next if row[CSV_WORDING_KEYS_COLUMN].blank?
|
66
|
+
|
67
|
+
raw_key = row[CSV_WORDING_KEYS_COLUMN].strip
|
68
|
+
key = keys[raw_key]
|
69
|
+
next if key.nil? || added_keys[raw_key]
|
70
|
+
|
71
|
+
locales.each do |locale|
|
72
|
+
value = row[locale]
|
73
|
+
next if export_request.bypass_empty_values && value.blank?
|
74
|
+
|
75
|
+
comment = row["#{COMMENT_KEY_COLUMN_IDENTIFIER} #{locale}"]
|
76
|
+
wording[locale].add_wording(key: key, value: value&.strip, comment: comment)
|
77
|
+
end
|
78
|
+
added_keys[raw_key] = true
|
79
|
+
end
|
80
|
+
wording
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AdLocalize
|
3
|
+
module Parsers
|
4
|
+
class KeyParser
|
5
|
+
PLURAL_KEY_REGEXP = /\#\#\{([A-Za-z]+)\}/.freeze
|
6
|
+
ADAPTIVE_KEY_REGEXP = /\#\#\{(\d+)\}/.freeze
|
7
|
+
# see https://developer.apple.com/documentation/bundleresources/information_property_list
|
8
|
+
INFO_PLIST_KEY_REGEXP = /(NS.+UsageDescription)|(CF.+Name)|NFCReaderUsageDescription/.freeze
|
9
|
+
|
10
|
+
def call(raw_key:)
|
11
|
+
type = compute_type(raw_key: raw_key)
|
12
|
+
label = compute_label(raw_key: raw_key, type: type)
|
13
|
+
variant_name = compute_variant_name(raw_key: raw_key, type: type)
|
14
|
+
Entities::Key.new(id: raw_key, label: label, type: type, variant_name: variant_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def plural?(raw_key:)
|
18
|
+
!raw_key.match(PLURAL_KEY_REGEXP).nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def adaptive?(raw_key:)
|
22
|
+
!raw_key.match(ADAPTIVE_KEY_REGEXP).nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def info_plist?(raw_key:)
|
26
|
+
!raw_key.match(INFO_PLIST_KEY_REGEXP).nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def compute_type(raw_key:)
|
30
|
+
if plural?(raw_key: raw_key)
|
31
|
+
Entities::WordingType::PLURAL
|
32
|
+
elsif adaptive?(raw_key: raw_key)
|
33
|
+
Entities::WordingType::ADAPTIVE
|
34
|
+
elsif info_plist?(raw_key: raw_key)
|
35
|
+
Entities::WordingType::INFO_PLIST
|
36
|
+
else
|
37
|
+
Entities::WordingType::SINGULAR
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def compute_label(raw_key:, type:)
|
42
|
+
case type
|
43
|
+
when Entities::WordingType::PLURAL
|
44
|
+
raw_key.gsub(PLURAL_KEY_REGEXP, '')
|
45
|
+
when Entities::WordingType::ADAPTIVE
|
46
|
+
raw_key.gsub(ADAPTIVE_KEY_REGEXP, '')
|
47
|
+
else
|
48
|
+
raw_key
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def compute_variant_name(raw_key:, type:)
|
53
|
+
case type
|
54
|
+
when Entities::WordingType::PLURAL
|
55
|
+
raw_key.match(PLURAL_KEY_REGEXP)&.captures&.first
|
56
|
+
when Entities::WordingType::ADAPTIVE
|
57
|
+
raw_key.match(ADAPTIVE_KEY_REGEXP)&.captures&.first
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module AdLocalize
|
3
|
+
module Repositories
|
4
|
+
class DriveRepository
|
5
|
+
def initialize
|
6
|
+
@drive_service = Google::Apis::DriveV3::DriveService.new
|
7
|
+
@sheet_service = Google::Apis::SheetsV4::SheetsService.new
|
8
|
+
if ENV['GOOGLE_APPLICATION_CREDENTIALS']
|
9
|
+
drive_scope = [Google::Apis::DriveV3::AUTH_DRIVE_READONLY]
|
10
|
+
sheet_scope = [Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY]
|
11
|
+
@drive_service.authorization = Google::Auth.get_application_default(drive_scope)
|
12
|
+
@sheet_service.authorization = Google::Auth.get_application_default(sheet_scope)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def download_sheets_by_id(spreadsheet_id:, sheet_ids:)
|
17
|
+
sheet_ids.filter_map do |sheet_id|
|
18
|
+
begin
|
19
|
+
url = export_url(spreadsheet_id: spreadsheet_id, sheet_id: sheet_id)
|
20
|
+
string = @drive_service.http(:get, url, options: { retries: 3, max_elapsed_time: 60 })
|
21
|
+
next unless string
|
22
|
+
|
23
|
+
tempfile = Tempfile.new
|
24
|
+
tempfile.write(string)
|
25
|
+
tempfile.rewind
|
26
|
+
tempfile
|
27
|
+
rescue => e
|
28
|
+
LOGGER.error("Cannot download sheet with id #{sheet_id}. Error: #{e.message}")
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def download_all_sheets(spreadsheet_id:)
|
35
|
+
begin
|
36
|
+
spreadsheet = @sheet_service.get_spreadsheet(spreadsheet_id)
|
37
|
+
sheet_ids = spreadsheet.sheets.map { |sheet| sheet.properties.sheet_id }
|
38
|
+
LOGGER.debug("#{sheet_ids.size} sheets in the spreadsheet")
|
39
|
+
download_sheets_by_id(spreadsheet_id: spreadsheet_id, sheet_ids: sheet_ids)
|
40
|
+
rescue => e
|
41
|
+
LOGGER.error("Cannot download sheets. Error: #{e.message}")
|
42
|
+
[]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def export_url(spreadsheet_id:, sheet_id:)
|
49
|
+
"https://docs.google.com/spreadsheets/d/#{spreadsheet_id}/export?format=csv&gid=#{sheet_id}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,90 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Requests
|
3
4
|
class ExportRequest
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@merge_policy = nil
|
20
|
-
end
|
21
|
-
end
|
5
|
+
DEFAULTS = {
|
6
|
+
locales: [],
|
7
|
+
bypass_empty_values: false,
|
8
|
+
csv_paths: [],
|
9
|
+
merge_policy: Interactors::MergeWordings::DEFAULT_POLICY,
|
10
|
+
output_path: Pathname.new('exports'),
|
11
|
+
spreadsheet_id: nil,
|
12
|
+
sheet_ids: %w[0],
|
13
|
+
export_all: false,
|
14
|
+
verbose: false,
|
15
|
+
platforms: Entities::Platform::SUPPORTED_PLATFORMS,
|
16
|
+
downloaded_csvs: []
|
17
|
+
}
|
18
|
+
|
19
|
+
attr_accessor :output_dir
|
22
20
|
|
23
21
|
attr_reader(
|
24
22
|
:locales,
|
25
|
-
:
|
26
|
-
:
|
23
|
+
:bypass_empty_values,
|
24
|
+
:csv_paths,
|
25
|
+
:merge_policy,
|
27
26
|
:output_path,
|
27
|
+
:platforms,
|
28
|
+
:spreadsheet_id,
|
29
|
+
:sheet_ids,
|
30
|
+
:export_all,
|
28
31
|
:verbose,
|
29
|
-
:
|
32
|
+
:downloaded_csvs
|
30
33
|
)
|
31
34
|
|
32
|
-
|
35
|
+
def initialize
|
36
|
+
@locales = DEFAULTS[:locales]
|
37
|
+
@bypass_empty_values = DEFAULTS[:bypass_empty_values]
|
38
|
+
@csv_paths = DEFAULTS[:csv_paths]
|
39
|
+
@merge_policy = DEFAULTS[:merge_policy]
|
40
|
+
@output_path = DEFAULTS[:output_path]
|
41
|
+
@platforms = DEFAULTS[:platforms]
|
42
|
+
@spreadsheet_id = DEFAULTS[:spreadsheet_id]
|
43
|
+
@sheet_ids = DEFAULTS[:sheet_ids]
|
44
|
+
@export_all = DEFAULTS[:export_all]
|
45
|
+
@verbose = DEFAULTS[:verbose]
|
46
|
+
@downloaded_csvs = DEFAULTS[:downloaded_csvs]
|
47
|
+
end
|
48
|
+
|
49
|
+
def locales=(value)
|
50
|
+
return unless value.is_a? Array
|
51
|
+
|
52
|
+
@locales = value.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def bypass_empty_values=(value)
|
56
|
+
@bypass_empty_values = [true, 'true'].include?(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def csv_paths=(value)
|
60
|
+
return unless value.is_a? Array
|
61
|
+
|
62
|
+
@csv_paths = value.compact.map { |path| Pathname.new(path) }
|
63
|
+
end
|
33
64
|
|
34
|
-
def
|
35
|
-
|
65
|
+
def merge_policy=(value)
|
66
|
+
@merge_policy = value unless value.blank?
|
36
67
|
end
|
37
68
|
|
38
|
-
def
|
39
|
-
|
69
|
+
def output_path=(value)
|
70
|
+
@output_path = Pathname.new(value) unless value.blank?
|
40
71
|
end
|
41
72
|
|
42
|
-
def
|
43
|
-
|
73
|
+
def platforms=(value)
|
74
|
+
return unless value.is_a? Array
|
75
|
+
|
76
|
+
@platforms = value.compact & DEFAULTS[:platforms]
|
77
|
+
end
|
78
|
+
|
79
|
+
def spreadsheet_id=(value)
|
80
|
+
@spreadsheet_id = value unless value.blank?
|
44
81
|
end
|
45
82
|
|
46
|
-
def
|
47
|
-
|
83
|
+
def sheet_ids=(value)
|
84
|
+
return unless value.is_a? Array
|
85
|
+
|
86
|
+
@sheet_ids = value.compact
|
48
87
|
end
|
49
88
|
|
50
|
-
def
|
51
|
-
|
89
|
+
def export_all=(value)
|
90
|
+
@export_all = [true, 'true'].include?(value)
|
52
91
|
end
|
53
92
|
|
54
|
-
def verbose
|
55
|
-
verbose
|
93
|
+
def verbose=(value)
|
94
|
+
@verbose = [true, 'true'].include?(value)
|
56
95
|
end
|
57
96
|
|
58
|
-
|
97
|
+
def downloaded_csvs=(value)
|
98
|
+
return unless value.is_a? Array
|
59
99
|
|
60
|
-
|
61
|
-
has_csv_files? && (@csv_paths.size == 1 || (@csv_paths.size > 1 && @merge_policy&.valid?))
|
100
|
+
@downloaded_csvs = value.compact
|
62
101
|
end
|
63
102
|
|
64
|
-
def
|
65
|
-
|
103
|
+
def has_sheets?
|
104
|
+
spreadsheet_id.present?
|
66
105
|
end
|
67
106
|
|
68
|
-
def
|
69
|
-
|
107
|
+
def has_csv_paths?
|
108
|
+
all_csv_paths.present?
|
70
109
|
end
|
71
110
|
|
72
|
-
def
|
73
|
-
|
111
|
+
def many_platforms?
|
112
|
+
platforms.size > 1
|
74
113
|
end
|
75
114
|
|
76
|
-
def
|
77
|
-
|
115
|
+
def all_csv_paths
|
116
|
+
csv_paths + downloaded_csvs.map(&:path)
|
78
117
|
end
|
79
118
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
119
|
+
def to_s
|
120
|
+
"locales: #{locales}, " \
|
121
|
+
"bypass_empty_values: #{bypass_empty_values}, " \
|
122
|
+
"csv_paths: #{csv_paths}, " \
|
123
|
+
"merge_policy: #{merge_policy}, " \
|
124
|
+
"output_path: #{output_path}, " \
|
125
|
+
"spreadsheet_id: #{spreadsheet_id}, " \
|
126
|
+
"sheet_ids: #{sheet_ids}, " \
|
127
|
+
"export_all: #{export_all}, " \
|
128
|
+
"verbose: #{verbose}, " \
|
129
|
+
"platforms: #{platforms}, " \
|
130
|
+
"downloaded_csvs: #{downloaded_csvs}"
|
87
131
|
end
|
88
132
|
end
|
89
133
|
end
|
90
|
-
end
|
134
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
|
-
module
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
def sanitize_value(value:)
|
3
|
+
module Sanitizers
|
4
|
+
class IOSToAndroidSanitizer
|
5
|
+
def sanitize(value:)
|
7
6
|
return if value.blank?
|
7
|
+
|
8
8
|
processedValue = value.gsub(/(?<!\\)'/, ''') # match ' unless there is a \ before
|
9
9
|
processedValue = processedValue.gsub(/(?<!\\)\"/, '"') # match " unless there is a \ before
|
10
10
|
processedValue = processedValue.gsub(">", '>')
|
@@ -13,9 +13,9 @@ module AdLocalize
|
|
13
13
|
processedValue = processedValue.gsub(/(%(\d+\$)?@)/, '%\2s') # should match values like %1$s and %s
|
14
14
|
hasFormatting = hasFormatting || processedValue.match(/(%((\d+\$)?(\d+)?)i)/)
|
15
15
|
processedValue = processedValue.gsub(/(%((\d+\$)?(\d+)?)i)/, '%\2d') # should match values like %i, %3$i, %01i, %1$02i
|
16
|
-
# On Android, '%' must be escaped with a second '%' if and only if the string has at least one formatting pattern. In this specific case,
|
16
|
+
# On Android, '%' must be escaped with a second '%' if and only if the string has at least one formatting pattern. In this specific case,
|
17
17
|
# a Java formatting method will be used which interprets every non escaped '%' as the start of a formatting pattern.
|
18
|
-
if hasFormatting
|
18
|
+
if hasFormatting
|
19
19
|
processedValue = processedValue.gsub(/%(?!((\d+\$)?(s|(\d+)?d)))/, '%%') # negative lookahead: identifies when user really wants to display a %
|
20
20
|
else
|
21
21
|
processedValue = processedValue.gsub(/%(?!((\d+\$)?(s|(\d+)?d)))/, '%') # negative lookahead: identifies when user really wants to display a %
|
@@ -27,4 +27,4 @@ module AdLocalize
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Serializers
|
3
|
-
class InfoPlistSerializer
|
4
|
-
include WithTemplate
|
5
|
-
|
4
|
+
class InfoPlistSerializer < TemplatedSerializer
|
6
5
|
INFO_PLIST_FILENAME = "InfoPlist.strings".freeze
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
|
8
|
+
super(sanitizer: Sanitizers::IOSSanitizer.new)
|
10
9
|
end
|
11
10
|
|
12
11
|
private
|
@@ -15,13 +14,12 @@ module AdLocalize
|
|
15
14
|
TEMPLATES_DIRECTORY + "/ios/#{INFO_PLIST_FILENAME}.erb"
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
translations.map { |translation| @translation_mapper.map(translation: translation) }
|
17
|
+
def variable_binding(locale_wording:)
|
18
|
+
translations = locale_wording.info_plists.map do |_, translation|
|
19
|
+
map_simple_wording(translation: translation)
|
20
|
+
end
|
21
|
+
{ translations: translations }
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
27
|
-
end
|
25
|
+
end
|
@@ -1,12 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Serializers
|
3
4
|
class JSONSerializer
|
4
|
-
def initialize
|
5
|
-
@locale_wording_to_hash = Mappers::LocaleWordingToHash.new
|
6
|
-
end
|
7
|
-
|
8
5
|
def render(locale_wording:)
|
9
|
-
|
6
|
+
content = Mappers::LocaleWordingToHash.new.map(locale_wording: locale_wording)
|
7
|
+
content.to_json
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
@@ -1,12 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Serializers
|
3
|
-
class LocalizableStringsSerializer
|
4
|
-
include WithTemplate
|
5
|
-
|
4
|
+
class LocalizableStringsSerializer < TemplatedSerializer
|
6
5
|
LOCALIZABLE_STRINGS_FILENAME = "Localizable.strings".freeze
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
|
8
|
+
super(sanitizer: Sanitizers::IOSSanitizer.new)
|
10
9
|
end
|
11
10
|
|
12
11
|
private
|
@@ -15,13 +14,12 @@ module AdLocalize
|
|
15
14
|
TEMPLATES_DIRECTORY + "/ios/#{LOCALIZABLE_STRINGS_FILENAME}.erb"
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
translations.map { |translation| @translation_mapper.map(translation: translation) }
|
17
|
+
def variable_binding(locale_wording:)
|
18
|
+
translations = locale_wording.singulars.map do |_, translation|
|
19
|
+
map_simple_wording(translation: translation)
|
20
|
+
end
|
21
|
+
{ translations: translations }
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
27
|
-
end
|
25
|
+
end
|
@@ -1,13 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Serializers
|
3
|
-
class LocalizableStringsdictSerializer
|
4
|
-
include WithTemplate
|
5
|
-
|
4
|
+
class LocalizableStringsdictSerializer < TemplatedSerializer
|
6
5
|
LOCALIZABLE_STRINGSDICT_FILENAME = "Localizable.stringsdict".freeze
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
|
10
|
-
@translation_group_mapper = Mappers::TranslationGroupMapper.new(translation_mapper: @translation_mapper)
|
8
|
+
super(sanitizer: Sanitizers::IOSSanitizer.new)
|
11
9
|
end
|
12
10
|
|
13
11
|
private
|
@@ -16,20 +14,15 @@ module AdLocalize
|
|
16
14
|
TEMPLATES_DIRECTORY + "/ios/#{LOCALIZABLE_STRINGSDICT_FILENAME}.erb"
|
17
15
|
end
|
18
16
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
plurals.map { |label, translations| @translation_group_mapper.map(label: label, translations: translations) }
|
28
|
-
end
|
29
|
-
|
30
|
-
def map_adaptives(adaptives:)
|
31
|
-
adaptives.map { |label, translations| @translation_group_mapper.map(label: label, translations: translations) }
|
17
|
+
def variable_binding(locale_wording:)
|
18
|
+
plurals = locale_wording.plurals.map do |label, translations|
|
19
|
+
map_compound_wording(label: label, translations: translations)
|
20
|
+
end
|
21
|
+
adaptives = locale_wording.adaptives.map do |label, translations|
|
22
|
+
map_compound_wording(label: label, translations: translations)
|
23
|
+
end
|
24
|
+
{ plurals: plurals, adaptives: adaptives }
|
32
25
|
end
|
33
26
|
end
|
34
27
|
end
|
35
|
-
end
|
28
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module AdLocalize
|
2
3
|
module Serializers
|
3
|
-
class PropertiesSerializer
|
4
|
-
include WithTemplate
|
5
|
-
|
4
|
+
class PropertiesSerializer < TemplatedSerializer
|
6
5
|
def initialize
|
7
|
-
|
6
|
+
super(sanitizer: Sanitizers::PassThroughSanitizer.new)
|
8
7
|
end
|
9
8
|
|
10
9
|
private
|
@@ -13,13 +12,12 @@ module AdLocalize
|
|
13
12
|
TEMPLATES_DIRECTORY + "/properties/template.properties.erb"
|
14
13
|
end
|
15
14
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
translations.map { |translation| @translation_mapper.map(translation: translation) }
|
15
|
+
def variable_binding(locale_wording:)
|
16
|
+
singulars = locale_wording.singulars.map do |_, translation|
|
17
|
+
map_simple_wording(translation: translation)
|
18
|
+
end
|
19
|
+
{ translations: singulars }
|
22
20
|
end
|
23
21
|
end
|
24
22
|
end
|
25
|
-
end
|
23
|
+
end
|