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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +6 -3
  3. data/.rubocop.yml +5 -0
  4. data/.rubocop_todo.yml +319 -0
  5. data/CHANGELOG.md +70 -15
  6. data/Gemfile +1 -0
  7. data/Gemfile.lock +63 -61
  8. data/README.md +37 -10
  9. data/Rakefile +1 -0
  10. data/ad_localize.gemspec +14 -13
  11. data/bin/console +1 -1
  12. data/exe/ad_localize +2 -1
  13. data/lib/ad_localize/ad_logger.rb +5 -10
  14. data/lib/ad_localize/cli.rb +10 -3
  15. data/lib/ad_localize/entities/key.rb +3 -76
  16. data/lib/ad_localize/entities/locale_wording.rb +32 -52
  17. data/lib/ad_localize/entities/platform.rb +13 -0
  18. data/lib/ad_localize/entities/simple_wording.rb +6 -0
  19. data/lib/ad_localize/entities/wording_type.rb +11 -0
  20. data/lib/ad_localize/interactors/base_generate_files.rb +37 -0
  21. data/lib/ad_localize/interactors/download_spreadsheets.rb +20 -0
  22. data/lib/ad_localize/interactors/export_wording.rb +27 -18
  23. data/lib/ad_localize/interactors/generate_info_plist.rb +24 -0
  24. data/lib/ad_localize/interactors/generate_ios_files.rb +12 -0
  25. data/lib/ad_localize/interactors/generate_json.rb +20 -0
  26. data/lib/ad_localize/interactors/generate_localizable_strings.rb +24 -0
  27. data/lib/ad_localize/interactors/generate_localizable_strings_dict.rb +24 -0
  28. data/lib/ad_localize/interactors/generate_properties.rb +20 -0
  29. data/lib/ad_localize/interactors/generate_strings.rb +25 -0
  30. data/lib/ad_localize/interactors/generate_yaml.rb +20 -0
  31. data/lib/ad_localize/interactors/merge_wordings.rb +49 -18
  32. data/lib/ad_localize/interactors/parse_csv_files.rb +22 -0
  33. data/lib/ad_localize/interactors/process_export_request.rb +21 -0
  34. data/lib/ad_localize/mappers/locale_wording_to_hash.rb +45 -24
  35. data/lib/ad_localize/mappers/options_to_export_request.rb +14 -21
  36. data/lib/ad_localize/option_handler.rb +50 -26
  37. data/lib/ad_localize/parsers/csv_parser.rb +84 -0
  38. data/lib/ad_localize/parsers/key_parser.rb +62 -0
  39. data/lib/ad_localize/repositories/drive_repository.rb +53 -0
  40. data/lib/ad_localize/repositories/file_system_repository.rb +2 -1
  41. data/lib/ad_localize/requests/export_request.rb +97 -53
  42. data/lib/ad_localize/sanitizers/ios_sanitizer.rb +12 -0
  43. data/lib/ad_localize/{mappers/android_translation_mapper.rb → sanitizers/ios_to_android_sanitizer.rb} +8 -8
  44. data/lib/ad_localize/sanitizers/pass_through_sanitizer.rb +10 -0
  45. data/lib/ad_localize/serializers/info_plist_serializer.rb +9 -11
  46. data/lib/ad_localize/serializers/json_serializer.rb +3 -5
  47. data/lib/ad_localize/serializers/localizable_strings_serializer.rb +9 -11
  48. data/lib/ad_localize/serializers/localizable_stringsdict_serializer.rb +12 -19
  49. data/lib/ad_localize/serializers/properties_serializer.rb +9 -11
  50. data/lib/ad_localize/serializers/strings_serializer.rb +12 -21
  51. data/lib/ad_localize/serializers/templated_serializer.rb +51 -0
  52. data/lib/ad_localize/serializers/yaml_serializer.rb +4 -6
  53. data/lib/ad_localize/templates/android/strings.xml.erb +6 -6
  54. data/lib/ad_localize/templates/ios/Localizable.stringsdict.erb +14 -14
  55. data/lib/ad_localize/version.rb +2 -1
  56. data/lib/ad_localize/view_models/compound_wording_view_model.rb +2 -0
  57. data/lib/ad_localize/view_models/simple_wording_view_model.rb +2 -0
  58. data/lib/ad_localize.rb +33 -34
  59. metadata +86 -69
  60. data/lib/ad_localize/constant.rb +0 -6
  61. data/lib/ad_localize/entities/translation.rb +0 -32
  62. data/lib/ad_localize/entities/wording.rb +0 -24
  63. data/lib/ad_localize/interactors/execute_export_request.rb +0 -43
  64. data/lib/ad_localize/interactors/export_csv_files.rb +0 -17
  65. data/lib/ad_localize/interactors/export_g_spreadsheet.rb +0 -59
  66. data/lib/ad_localize/interactors/platforms/export_android_locale_wording.rb +0 -43
  67. data/lib/ad_localize/interactors/platforms/export_csv_locale_wording.rb +0 -25
  68. data/lib/ad_localize/interactors/platforms/export_ios_locale_wording.rb +0 -66
  69. data/lib/ad_localize/interactors/platforms/export_json_locale_wording.rb +0 -27
  70. data/lib/ad_localize/interactors/platforms/export_platform_factory.rb +0 -50
  71. data/lib/ad_localize/interactors/platforms/export_properties_locale_wording.rb +0 -33
  72. data/lib/ad_localize/interactors/platforms/export_yaml_locale_wording.rb +0 -27
  73. data/lib/ad_localize/mappers/csv_path_to_wording.rb +0 -73
  74. data/lib/ad_localize/mappers/ios_translation_mapper.rb +0 -12
  75. data/lib/ad_localize/mappers/translation_group_mapper.rb +0 -14
  76. data/lib/ad_localize/mappers/translation_mapper.rb +0 -30
  77. data/lib/ad_localize/mappers/value_range_to_wording.rb +0 -67
  78. data/lib/ad_localize/repositories/g_sheets_repository.rb +0 -44
  79. data/lib/ad_localize/requests/g_spreadsheet_options.rb +0 -47
  80. data/lib/ad_localize/requests/merge_policy.rb +0 -28
  81. data/lib/ad_localize/serializers/with_template.rb +0 -19
  82. data/lib/ad_localize/validators/key_validator.rb +0 -31
  83. data/lib/ad_localize/view_models/translation_group_view_model.rb +0 -19
  84. data/lib/ad_localize/view_models/translation_view_model.rb +0 -23
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class BaseGenerateFiles
5
+ def initialize(serializer:)
6
+ @serializer = serializer
7
+ raise MissingArgument.new('Missing Serializer') unless serializer
8
+
9
+ @file_system_repository = Repositories::FileSystemRepository.new
10
+ end
11
+
12
+ def call(wording:, export_request:)
13
+ wording.each do |locale, locale_wording|
14
+ next unless has_wording?(locale_wording: locale_wording)
15
+
16
+ path = output_path(locale_wording: locale_wording, export_request: export_request)
17
+ filename = path.basename
18
+ LOGGER.debug("[#{locale}] Generating #{filename}")
19
+ @file_system_repository.create_directory(path: path.dirname)
20
+ content = @serializer.render(locale_wording: locale_wording)
21
+ @file_system_repository.write(content: content, path: path)
22
+ LOGGER.debug("[#{locale}] #{filename} done !")
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def has_wording?(locale_wording:)
29
+ raise 'override me!'
30
+ end
31
+
32
+ def output_path(locale_wording:, export_request:)
33
+ raise 'override me!'
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class DownloadSpreadsheets
5
+ def initialize
6
+ @drive_repository = Repositories::DriveRepository.new
7
+ end
8
+
9
+ def call(export_request:)
10
+ LOGGER.debug("Downloading spreadsheets")
11
+ if export_request.export_all
12
+ @drive_repository.download_all_sheets(spreadsheet_id: export_request.spreadsheet_id)
13
+ else
14
+ @drive_repository.download_sheets_by_id(spreadsheet_id: export_request.spreadsheet_id,
15
+ sheet_ids: export_request.sheet_ids)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,31 +1,40 @@
1
+ # frozen_string_literal: true
1
2
  module AdLocalize
2
3
  module Interactors
3
4
  class ExportWording
4
- def initialize
5
- @export_platform_factory = Platforms::ExportPlatformFactory.new
6
- end
7
-
8
5
  def call(export_request:, wording:)
9
- LOGGER.debug("Starting export wording")
10
6
  export_request.platforms.each do |platform|
11
- platform_dir = compute_platform_dir(export_request: export_request, platform: platform)
12
- export_platform = @export_platform_factory.build(platform: platform)
13
- if export_platform.should_export_locale_by_locale?
14
- locales = export_request.locales.size.zero? ? wording.locales : wording.locales & export_request.locales
15
- locales.each do |locale|
16
- export_platform.call(wording: wording, locale: locale, platform_dir: platform_dir)
17
- end
18
- else
19
- export_platform.call(export_request: export_request, platform_dir: platform_dir)
20
- end
7
+ configure_output_directory(platform: platform, export_request: export_request)
8
+ LOGGER.debug("[#{platform}] Start exporting in #{export_request.output_dir}")
9
+ export_platform(wording: wording, platform: platform, export_request: export_request)
10
+ LOGGER.debug("[#{platform}] done !")
21
11
  end
22
12
  end
23
13
 
24
14
  private
25
15
 
26
- def compute_platform_dir(export_request:, platform:)
27
- export_request.multiple_platforms? ? export_request.output_path.join(platform.to_s) : export_request.output_path
16
+ def configure_output_directory(platform:, export_request:)
17
+ if export_request.many_platforms?
18
+ export_request.output_dir = export_request.output_path.join(platform)
19
+ else
20
+ export_request.output_dir = export_request.output_path
21
+ end
22
+ end
23
+
24
+ def export_platform(wording:, platform:, export_request:)
25
+ case platform
26
+ when Entities::Platform::IOS
27
+ GenerateIOSFiles.new.call(wording: wording, export_request: export_request)
28
+ when Entities::Platform::ANDROID
29
+ GenerateStrings.new.call(wording: wording, export_request: export_request)
30
+ when Entities::Platform::YML
31
+ GenerateYAML.new.call(wording: wording, export_request: export_request)
32
+ when Entities::Platform::JSON
33
+ GenerateJSON.new.call(wording: wording, export_request: export_request)
34
+ when Entities::Platform::PROPERTIES
35
+ GenerateProperties.new.call(wording: wording, export_request: export_request)
36
+ end
28
37
  end
29
38
  end
30
39
  end
31
- end
40
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateInfoPlist < BaseGenerateFiles
5
+ INFO_PLIST_FILENAME = "InfoPlist.strings".freeze
6
+ LOCALE_DIRECTORY_CONVENTION = "%{locale}.lproj".freeze
7
+
8
+ def initialize
9
+ super(serializer: Serializers::InfoPlistSerializer.new)
10
+ end
11
+
12
+ private
13
+
14
+ def has_wording?(locale_wording:)
15
+ locale_wording.info_plists.any?
16
+ end
17
+
18
+ def output_path(locale_wording:, export_request:)
19
+ locale_directory_name = LOCALE_DIRECTORY_CONVENTION % { locale: locale_wording.locale }
20
+ export_request.output_dir.join(locale_directory_name, INFO_PLIST_FILENAME)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateIOSFiles
5
+ def call(wording:, export_request:)
6
+ GenerateInfoPlist.new.call(wording: wording, export_request: export_request)
7
+ GenerateLocalizableStrings.new.call(wording: wording, export_request: export_request)
8
+ GenerateLocalizableStringsDict.new.call(wording: wording, export_request: export_request)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateJSON < BaseGenerateFiles
5
+ def initialize
6
+ super(serializer: Serializers::JSONSerializer.new)
7
+ end
8
+
9
+ private
10
+
11
+ def has_wording?(locale_wording:)
12
+ locale_wording.singulars.any? || locale_wording.plurals.any?
13
+ end
14
+
15
+ def output_path(locale_wording:, export_request:)
16
+ export_request.output_dir.join("#{locale_wording.locale}.json")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateLocalizableStrings < BaseGenerateFiles
5
+ LOCALIZABLE_STRINGS_FILENAME = "Localizable.strings".freeze
6
+ LOCALE_DIRECTORY_CONVENTION = "%{locale}.lproj".freeze
7
+
8
+ def initialize
9
+ super(serializer: Serializers::LocalizableStringsSerializer.new)
10
+ end
11
+
12
+ private
13
+
14
+ def has_wording?(locale_wording:)
15
+ locale_wording.singulars.any?
16
+ end
17
+
18
+ def output_path(locale_wording:, export_request:)
19
+ locale_directory_name = LOCALE_DIRECTORY_CONVENTION % { locale: locale_wording.locale }
20
+ export_request.output_dir.join(locale_directory_name, LOCALIZABLE_STRINGS_FILENAME)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateLocalizableStringsDict < BaseGenerateFiles
5
+ LOCALIZABLE_STRINGSDICT_FILENAME = "Localizable.stringsdict".freeze
6
+ LOCALE_DIRECTORY_CONVENTION = "%{locale}.lproj".freeze
7
+
8
+ def initialize
9
+ super(serializer: Serializers::LocalizableStringsdictSerializer.new)
10
+ end
11
+
12
+ private
13
+
14
+ def has_wording?(locale_wording:)
15
+ locale_wording.plurals.any? || locale_wording.adaptives.any?
16
+ end
17
+
18
+ def output_path(locale_wording:, export_request:)
19
+ locale_directory_name = LOCALE_DIRECTORY_CONVENTION % { locale: locale_wording.locale }
20
+ export_request.output_dir.join(locale_directory_name, LOCALIZABLE_STRINGSDICT_FILENAME)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateProperties < BaseGenerateFiles
5
+ def initialize
6
+ super(serializer: Serializers::PropertiesSerializer.new)
7
+ end
8
+
9
+ private
10
+
11
+ def has_wording?(locale_wording:)
12
+ locale_wording.singulars.any?
13
+ end
14
+
15
+ def output_path(locale_wording:, export_request:)
16
+ export_request.output_dir.join("#{locale_wording.locale}.properties")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateStrings < BaseGenerateFiles
5
+ STRINGS_FILENAME = 'strings.xml'.freeze
6
+ LOCALE_DIRECTORY_CONVENTION = "values".freeze
7
+
8
+ def initialize
9
+ super(serializer: Serializers::StringsSerializer.new)
10
+ end
11
+
12
+ private
13
+
14
+ def has_wording?(locale_wording:)
15
+ locale_wording.singulars.any? || locale_wording.plurals.any?
16
+ end
17
+
18
+ def output_path(locale_wording:, export_request:)
19
+ locale_dir = LOCALE_DIRECTORY_CONVENTION
20
+ locale_dir += "-#{locale_wording.locale}" if !locale_wording.is_default
21
+ export_request.output_dir.join(locale_dir, STRINGS_FILENAME)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class GenerateYAML < BaseGenerateFiles
5
+ def initialize
6
+ super(serializer: Serializers::YAMLSerializer.new)
7
+ end
8
+
9
+ private
10
+
11
+ def has_wording?(locale_wording:)
12
+ locale_wording.singulars.any? || locale_wording.plurals.any?
13
+ end
14
+
15
+ def output_path(locale_wording:, export_request:)
16
+ export_request.output_dir.join("#{locale_wording.locale}.yml")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,13 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  module AdLocalize
2
3
  module Interactors
3
4
  class MergeWordings
4
5
  REPLACE_MERGE_POLICY = 'replace'.freeze
5
6
  KEEP_MERGE_POLICY = 'keep'.freeze
6
7
  MERGE_POLICIES = [KEEP_MERGE_POLICY, REPLACE_MERGE_POLICY]
7
-
8
- def self.valid_policy?(policy:)
9
- MERGE_POLICIES.include?(policy)
10
- end
8
+ DEFAULT_POLICY = KEEP_MERGE_POLICY
11
9
 
12
10
  def call(wordings:, merge_policy:)
13
11
  if wordings.size == 1
@@ -20,24 +18,57 @@ module AdLocalize
20
18
 
21
19
  private
22
20
 
21
+ def merge_simple_wordings(reference_list:, new_list:, merge_policy:)
22
+ new_list.each do |label, new_translation|
23
+ translation_reference = reference_list[label]
24
+ if translation_reference.nil?
25
+ reference_list[label] = new_translation
26
+ elsif merge_policy == REPLACE_MERGE_POLICY
27
+ LOGGER.warn("[MERGE] #{label} value changed from #{translation_reference.value} to #{new_translation.value}")
28
+ reference_list[label].value = new_translation.value
29
+ end
30
+ end
31
+ end
32
+
33
+ def merge_compound_wordings(reference_hash:, new_hash:, merge_policy:)
34
+ new_hash.each do |new_label, new_list|
35
+ if reference_hash[new_label].nil?
36
+ reference_hash[new_label] = new_list
37
+ elsif merge_policy == REPLACE_MERGE_POLICY
38
+ merge_simple_wordings(reference_list: reference_hash[new_label], new_list: new_list, merge_policy: merge_policy)
39
+ end
40
+ end
41
+ end
42
+
23
43
  def merge_many(wordings:, merge_policy:)
24
- final_wording = wordings.first
44
+ wording_reference = wordings.first
25
45
  wordings[1..-1].each do |wording|
26
- wording.locale_wordings.each do |locale_wording|
27
- locale_wording_reference = final_wording.translations_for(locale: locale_wording.locale)
28
- locale_wording.translations.each do |translation|
29
- translation_reference = locale_wording_reference.translation_for(key: translation.key)
30
- if translation_reference.present?
31
- next unless merge_policy.replace?
32
- translation_reference.value = translation.value
33
- else
34
- locale_wording_reference.add_translation(translation: translation)
35
- end
36
- end
46
+ wording.each do |locale, locale_wording|
47
+ reference = wording_reference[locale]
48
+ merge_simple_wordings(
49
+ reference_list: reference.singulars,
50
+ new_list: locale_wording.singulars,
51
+ merge_policy: merge_policy
52
+ )
53
+ merge_simple_wordings(
54
+ reference_list: reference.info_plists,
55
+ new_list: locale_wording.info_plists,
56
+ merge_policy: merge_policy
57
+ )
58
+ merge_compound_wordings(
59
+ reference_hash: reference.plurals,
60
+ new_hash: locale_wording.plurals,
61
+ merge_policy: merge_policy
62
+ )
63
+ merge_compound_wordings(
64
+ reference_hash: reference.adaptives,
65
+ new_hash: locale_wording.adaptives,
66
+ merge_policy: merge_policy
67
+ )
37
68
  end
38
69
  end
39
- final_wording
70
+ wording_reference
40
71
  end
41
72
  end
42
73
  end
43
- end
74
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class ParseCSVFiles
5
+ def initialize
6
+ @csv_parser = Parsers::CSVParser.new
7
+ end
8
+
9
+ def call(export_request:)
10
+ csv_paths = export_request.all_csv_paths
11
+ LOGGER.debug("Will parse #{csv_paths.size} csv files")
12
+ wordings = csv_paths.filter_map do |csv_path|
13
+ @csv_parser.call(csv_path: csv_path, export_request: export_request)
14
+ end
15
+ LOGGER.debug("#{wordings.size} wording contents detected")
16
+ return if wordings.blank?
17
+
18
+ MergeWordings.new.call(wordings: wordings, merge_policy: export_request.merge_policy)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module AdLocalize
3
+ module Interactors
4
+ class ProcessExportRequest
5
+ def call(export_request:)
6
+ export_request.verbose ? LOGGER.debug! : LOGGER.info!
7
+ LOGGER.debug(export_request)
8
+ LOGGER.debug("Verify if there are CSV to process")
9
+ return unless export_request.has_csv_paths?
10
+
11
+ LOGGER.debug("Parse CSV files")
12
+ wording = ParseCSVFiles.new.call(export_request: export_request)
13
+ return unless wording
14
+
15
+ LOGGER.debug("Export wording")
16
+ ExportWording.new.call(export_request: export_request, wording: wording)
17
+ LOGGER.debug("End of export request execution")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,33 +1,54 @@
1
+ # frozen_string_literal: true
1
2
  module AdLocalize
2
3
  module Mappers
3
4
  class LocaleWordingToHash
4
5
  def map(locale_wording:)
5
- result = locale_wording.translations.each_with_object({}) do |translation, hash|
6
- inner_keys = translation.key.label.split('.')
7
- inner_keys.each_with_index do |inner_key, index|
8
- if index === inner_keys.count - 1
9
- if translation.key.plural?
10
- hash[translation.key.label] = {} unless hash.key? translation.key.label
11
- hash[translation.key.label][translation.key.plural_key] = translation.value
12
- else
13
- unless hash.is_a?(Hash)
14
- LOGGER.warn "Corrupted input. Trying to insert a value for key '#{translation.key.label}' but a value already exists for '#{inner_keys[0..index-1].join(".")}'. Skipping."
15
- break # skip this corrupted key
16
- end
17
- previous_value = hash[inner_key.to_s]
18
- if !previous_value.blank? && previous_value.is_a?(Hash)
19
- LOGGER.warn "Corrupted input. Trying to insert a value for key '#{translation.key.label}' but values already exist for keys '#{translation.key.label}.*'. Previous values will be lost."
20
- end
21
- hash[inner_key.to_s] = translation.value
22
- end
23
- else
24
- hash[inner_key] = {} if hash[inner_key].nil?
25
- hash = hash[inner_key]
26
- end
6
+ singulars_hash = map_singulars(simple_wordings: locale_wording.singulars.values)
7
+ plural_hash = map_plurals(coumpound_wordings: locale_wording.plurals)
8
+ locale_hash = singulars_hash.merge(plural_hash)
9
+ { locale_wording.locale => locale_hash }
10
+ end
11
+
12
+ private
13
+
14
+ def inner_keys(label:)
15
+ label.split('.')
16
+ end
17
+
18
+ def map_singulars(simple_wordings:)
19
+ map_translations(translations: simple_wordings) do |inner_keys, translation|
20
+ dotted_key_to_hash(inner_keys, { inner_keys.pop => translation.value })
21
+ end
22
+ end
23
+
24
+ def map_plurals(coumpound_wordings:)
25
+ result = {}
26
+ coumpound_wordings.each do |label, simple_wordings|
27
+ variants_hash = map_translations(translations: simple_wordings.values) do |keys, translation|
28
+ dotted_key_to_hash(keys, { translation.key.variant_name => translation.value })
27
29
  end
30
+ result.deep_merge!(variants_hash)
31
+ end
32
+ result
33
+ end
34
+
35
+ def map_translations(translations:)
36
+ translations.each_with_object({}) do |translation, hash|
37
+ keys = inner_keys(label: translation.key.label)
38
+ inner_keys_hash = yield(keys, translation)
39
+ hash.deep_merge!(inner_keys_hash)
40
+ end
41
+ end
42
+
43
+ def dotted_key_to_hash(array, h)
44
+ if array.size.zero?
45
+ h
46
+ elsif array.size == 1
47
+ { array.first => h }
48
+ elsif array.size > 1
49
+ { array.first => dotted_key_to_hash(array[1..-1], h) }
28
50
  end
29
- { locale_wording.locale => result }
30
51
  end
31
52
  end
32
53
  end
33
- end
54
+ end
@@ -1,28 +1,21 @@
1
+ # frozen_string_literal: true
1
2
  module AdLocalize
2
3
  module Mappers
3
4
  class OptionsToExportRequest
4
5
  def map(options:)
5
- Requests::ExportRequest.new(
6
- platforms: options[:only],
7
- g_spreadsheet_options: map_g_spreadsheet_options(options: options),
8
- verbose: options[:debug],
9
- output_path: options[:'target-dir'],
10
- merge_policy: options[:'merge-policy'],
11
- csv_paths: options[:csv_paths]
12
- )
13
- end
14
-
15
- private
16
-
17
- def map_g_spreadsheet_options(options:)
18
- return unless options[:'drive-key']
19
- Requests::GSpreadsheetOptions.new(
20
- spreadsheet_id: options[:'drive-key'],
21
- sheet_ids: options[:'sheets'],
22
- export_all: options[:'export-all-sheets'],
23
- service_account_config: ENV['GCLOUD_CLIENT_SECRET']
24
- )
6
+ request = Requests::ExportRequest.new
7
+ request.locales = options[:locales]
8
+ request.bypass_empty_values = options[:'non-empty-values']
9
+ request.csv_paths = options[:csv_paths]
10
+ request.merge_policy = options[:'merge-policy']
11
+ request.output_path = options[:'target-dir']
12
+ request.platforms = options[:only]
13
+ request.spreadsheet_id = options[:'drive-key']
14
+ request.sheet_ids = options[:sheets]
15
+ request.export_all = options[:'export-all-sheets']
16
+ request.verbose = options[:debug]
17
+ request
25
18
  end
26
19
  end
27
20
  end
28
- end
21
+ end
@@ -1,38 +1,62 @@
1
+ # frozen_string_literal: true
1
2
  module AdLocalize
2
3
  class OptionHandler
4
+ DEFAULT_OPTIONS = {
5
+ locales: Requests::ExportRequest::DEFAULTS[:locales],
6
+ :'non-empty-values' => Requests::ExportRequest::DEFAULTS[:bypass_empty_values],
7
+ csv_paths: Requests::ExportRequest::DEFAULTS[:csv_paths],
8
+ :'merge-policy' => Requests::ExportRequest::DEFAULTS[:merge_policy],
9
+ :'target-dir' => Requests::ExportRequest::DEFAULTS[:output_path],
10
+ :'drive-key' => Requests::ExportRequest::DEFAULTS[:spreadsheet_id],
11
+ sheets: Requests::ExportRequest::DEFAULTS[:sheet_ids],
12
+ :'export-all-sheets' => Requests::ExportRequest::DEFAULTS[:export_all],
13
+ debug: Requests::ExportRequest::DEFAULTS[:verbose],
14
+ only: Requests::ExportRequest::DEFAULTS[:platforms]
15
+ }
16
+
3
17
  def self.parse!(options)
4
- option_parser = OptionParser.new do |parser|
5
- parser.banner = "Usage: exe/ad_localize [options] file(s)"
18
+ args = DEFAULT_OPTIONS
19
+ export_all_option = <<~DOC
20
+ Export all sheets from spreadsheet specified by --drive-key option.
21
+ \tBy default, generates one export directory per sheet (see -m|--merge-sheets option to merge them).
22
+ \tAn GCLOUD_CLIENT_SECRET environment variable containing the client_secret.json content is needed.
23
+ DOC
24
+ merge_policy_option = <<~DOC
25
+ Merge specified csv (or sheets from --export-all) instead of exporting each csv.
26
+ \treplace: if a key is already defined, replace its value.
27
+ \tkeep: if a key is already defined, keep the previous value.
28
+ DOC
29
+ platforms_option = <<~DOC
30
+ PLATFORMS is a comma separated list.
31
+ \tOnly generate localisation files for the specified platforms.
32
+ \tSupported platforms : #{Entities::Platform::SUPPORTED_PLATFORMS.to_sentence}
33
+ DOC
6
34
 
7
- parser.on("-d", "--debug", TrueClass, "Run in debug mode")
8
- parser.on("-e", "--export-all-sheets", TrueClass,
9
- <<~DOC
10
- Export all sheets from spreadsheet specified by --drive-key option.
11
- \tBy default, generates one export directory per sheet (see -m|--merge-sheets option to merge them).
12
- \tAn GCLOUD_CLIENT_SECRET environment variable containing the client_secret.json content is needed.
13
- DOC
14
- )
15
- parser.on("-h", "--help", "Prints help") do
35
+ OptionParser.new do |parser|
36
+ parser.banner = 'Usage: exe/ad_localize [options] file(s)'
37
+ parser.on("-d", "--debug", TrueClass, 'Run in debug mode')
38
+ parser.on("-e", "--export-all-sheets", TrueClass, export_all_option)
39
+ parser.on("-h", "--help", 'Prints help') do
16
40
  puts parser
17
41
  exit
18
42
  end
19
- parser.on("-k", "--drive-key SPREADSHEET_ID", String, "Use google drive spreadsheets")
20
- parser.on("-m", "--merge-policy POLICY", String,
21
- <<~DOC
22
- Merge specified csv (or sheets from --export-all) instead of exporting each csv.
23
- \treplace: if a key is already defined, replace its value.
24
- \tkeep: if a key is already defined, keep the previous value.
25
- DOC
26
- )
27
- parser.on("-o", "--only PLATFORMS", Array, "PLATFORMS is a comma separated list. Only generate localisation files for the specified platforms. Supported platforms : #{Requests::ExportRequest::SUPPORTED_PLATFORMS.to_sentence}")
28
- parser.on("-s", "--sheets SHEET_IDS", Array, "SHEET_IDS is a comma separated list. Use a specific sheet id for Google Drive spreadsheets with several sheets")
29
- parser.on("-t", "--target-dir PATH", String, "Path to the target directory")
30
- end
43
+ parser.on("-k", "--drive-key SPREADSHEET_ID", String, 'Use google drive spreadsheets')
44
+ parser.on("-l", "--locales LOCALES", Array,
45
+ 'LOCALES is a comma separated list. Only generate localisation files for the specified locales')
46
+ parser.on("-m", "--merge-policy POLICY", Interactors::MergeWordings::MERGE_POLICIES, merge_policy_option)
47
+ parser.on("-o", "--only PLATFORMS", Array, platforms_option)
48
+ parser.on("-s", "--sheets SHEET_IDS", Array,
49
+ 'SHEET_IDS is a comma separated list. Use a specific sheet id for Google Drive spreadsheets with several sheets')
50
+ parser.on("-t", "--target-dir PATH", String, 'Path to the target directory')
51
+ parser.on("-v", "--version", 'Prints current version') do
52
+ puts AdLocalize::VERSION
53
+ exit
54
+ end
55
+ parser.on("-x", "--non-empty-values", TrueClass, 'Do not export keys with empty values (iOS only)')
56
+ end.parse!(options, into: args)
31
57
 
32
- args = {}
33
- option_parser.parse!(options, into: args)
34
58
  args[:csv_paths] = options
35
59
  return args
36
60
  end
37
61
  end
38
- end
62
+ end