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
@@ -1,24 +0,0 @@
1
- module AdLocalize
2
- module Entities
3
- class Wording
4
- attr_reader(:default_locale, :locale_wordings)
5
-
6
- def initialize(locale_wordings:, default_locale:)
7
- @locale_wordings = locale_wordings
8
- @default_locale = default_locale
9
- end
10
-
11
- def translations_for(locale:)
12
- @locale_wordings.find { |locale_wording| locale_wording.locale == locale }
13
- end
14
-
15
- def locales
16
- @locale_wordings.map(&:locale)
17
- end
18
-
19
- def default_locale?(locale:)
20
- locale == @default_locale
21
- end
22
- end
23
- end
24
- end
@@ -1,43 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- class ExecuteExportRequest
4
- def initialize(csv_path_to_wording: nil, value_range_to_wording: nil)
5
- @csv_path_to_wording = csv_path_to_wording
6
- @value_range_to_wording = value_range_to_wording
7
- end
8
-
9
- def call(export_request:)
10
- export_request.verbose? ? LOGGER.debug! : LOGGER.info!
11
- print_export_request(export_request: export_request) if export_request.verbose?
12
- LOGGER.debug("Checking request validity")
13
- return unless export_request.valid?
14
- if export_request.has_csv_files?
15
- ExportCSVFiles.new(csv_path_to_wording: @csv_path_to_wording).call(export_request: export_request)
16
- elsif export_request.has_g_spreadsheet_options?
17
- ExportGSpreadsheet.new(value_range_to_wording: @value_range_to_wording).call(export_request: export_request)
18
- end
19
- LOGGER.debug("End of export request execution")
20
- end
21
-
22
- private
23
-
24
- def print_export_request(export_request:)
25
- LOGGER.debug("Export Request info")
26
- LOGGER.debug("locales : #{export_request.locales.to_sentence}")
27
- LOGGER.debug("platforms : #{export_request.platforms.to_sentence}")
28
- LOGGER.debug("output_path : #{export_request.output_path}")
29
- LOGGER.debug("verbose : #{export_request.verbose}")
30
- LOGGER.debug("merge_policy : #{export_request.merge_policy&.policy}")
31
- LOGGER.debug("csv_paths : #{export_request.csv_paths.to_sentence}")
32
- if export_request.has_g_spreadsheet_options?
33
- g_options = export_request.g_spreadsheet_options
34
- LOGGER.debug("spreadsheet_id : #{g_options.spreadsheet_id}")
35
- LOGGER.debug("sheet_ids : #{g_options.sheet_ids.to_sentence}")
36
- LOGGER.debug("export_all: #{g_options.export_all}")
37
- LOGGER.debug("service_account: #{g_options.service_account_config.present?}")
38
- end
39
- LOGGER.debug("End Export Request info")
40
- end
41
- end
42
- end
43
- end
@@ -1,17 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- class ExportCSVFiles
4
- def initialize(csv_path_to_wording: nil)
5
- @csv_path_to_wording = csv_path_to_wording.presence || Mappers::CSVPathToWording.new
6
- @merge_wordings = MergeWordings.new
7
- end
8
-
9
- def call(export_request:)
10
- LOGGER.debug("Starting export csv files : #{export_request.csv_paths.to_sentence}")
11
- wordings = export_request.csv_paths.map { |csv_path| @csv_path_to_wording.map(csv_path: csv_path) }
12
- wording = @merge_wordings.call(wordings: wordings.compact, merge_policy: export_request.merge_policy)
13
- ExportWording.new.call(export_request: export_request, wording: wording)
14
- end
15
- end
16
- end
17
- end
@@ -1,59 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- class ExportGSpreadsheet
4
- def initialize(value_range_to_wording: nil)
5
- @value_range_to_wording = value_range_to_wording.presence || Mappers::ValueRangeToWording.new
6
- @g_sheets_repository = Repositories::GSheetsRepository.new
7
- end
8
-
9
- def call(export_request:)
10
- LOGGER.debug("Starting export google spreadsheet")
11
- if export_request.g_spreadsheet_options.service_account_config
12
- export_with_service_account(export_request: export_request)
13
- else
14
- export_without_service_account(export_request: export_request)
15
- end
16
- end
17
-
18
- private
19
-
20
- def export_with_service_account(export_request:)
21
- LOGGER.debug("Using service account")
22
- value_ranges = @g_sheets_repository.get_sheets_values(g_spreadsheet_options: export_request.g_spreadsheet_options)
23
- wordings = value_ranges.map { |value_range| @value_range_to_wording.map(value_range: value_range) }
24
- wording = MergeWordings.new.call(wordings: wordings.compact, merge_policy: export_request.merge_policy)
25
- ExportWording.new.call(export_request: export_request, wording: wording)
26
- end
27
-
28
- def export_without_service_account(export_request:)
29
- LOGGER.debug("Using direct access to spreadsheet")
30
- downloaded_files = export_request.g_spreadsheet_options.public_download_urls.map do |sheet_download_url|
31
- download_public_sheet(url: sheet_download_url)
32
- end
33
- export_request.csv_paths = downloaded_files.map(&:path)
34
- if export_request.has_csv_files?
35
- ExportCSVFiles.new.call(export_request: export_request)
36
- elsif export_request.has_empty_files?
37
- # When downloading an empty spreadsheet, the content type of the downloaded file is "inode/x-empty"
38
- LOGGER.warn("Your spreadsheet is empty. Add content and retry.")
39
- else
40
- # When shared configuration is misconfigured, the content type of the downloaded file is "text/html"
41
- LOGGER.error("Invalid export request. Check the spreadsheet share configuration")
42
- end
43
- downloaded_files.select { |downloaded_file| File.exist?(downloaded_file.path) }.each do |downloaded_file|
44
- downloaded_file.close
45
- downloaded_file.unlink
46
- end
47
- end
48
-
49
- def download_public_sheet(url:)
50
- tempfile = Tempfile.new
51
- URI.open(url) do |uri_io|
52
- tempfile.write uri_io.read
53
- tempfile.rewind
54
- end
55
- tempfile
56
- end
57
- end
58
- end
59
- end
@@ -1,43 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportAndroidLocaleWording
5
- STRINGS_FILENAME = 'strings.xml'.freeze
6
- LOCALE_DIRECTORY_CONVENTION = "values%{locale_suffix}".freeze
7
-
8
- def initialize
9
- @strings_serializer = Serializers::StringsSerializer.new
10
- @file_system_repository = Repositories::FileSystemRepository.new
11
- end
12
-
13
- def call(wording:, locale:, platform_dir:)
14
- LOGGER.debug("Starting export Android wording for locale #{locale}")
15
- locale_wording = wording.translations_for(locale: locale)
16
- return unless has_android_wording?(locale_wording: locale_wording)
17
-
18
- output_dir = compute_output_dir(wording: wording, locale: locale, platform_dir: platform_dir)
19
- @file_system_repository.create_directory(path: output_dir)
20
-
21
- content = @strings_serializer.render(locale_wording: locale_wording)
22
- @file_system_repository.write(content: content, path: output_dir.join(STRINGS_FILENAME))
23
- LOGGER.debug("#{STRINGS_FILENAME} done !")
24
- end
25
-
26
- def should_export_locale_by_locale?
27
- true
28
- end
29
-
30
- private
31
-
32
- def has_android_wording?(locale_wording:)
33
- locale_wording.has_singular_keys? || locale_wording.has_plural_keys?
34
- end
35
-
36
- def compute_output_dir(wording:, locale:, platform_dir:)
37
- locale_suffix = wording.default_locale?(locale: locale) ? '' : "-#{locale}"
38
- platform_dir.join(LOCALE_DIRECTORY_CONVENTION % { locale_suffix: locale_suffix })
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,25 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportCSVLocaleWording
5
- def initialize
6
- @file_system_repository = Repositories::FileSystemRepository.new
7
- end
8
-
9
- def call(export_request:, platform_dir:)
10
- LOGGER.debug("Starting export CSV wording")
11
- @file_system_repository.create_directory(path: platform_dir)
12
- export_request.csv_paths.each_with_index do |csv_path, i|
13
- file = File.basename("localization_#{i}.csv")
14
- FileUtils.cp(csv_path, platform_dir.join(file.to_s))
15
- end
16
- LOGGER.debug("CSV wording export done !")
17
- end
18
-
19
- def should_export_locale_by_locale?
20
- false
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,66 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportIOSLocaleWording
5
- INFO_PLIST_FILENAME = "InfoPlist.strings".freeze
6
- LOCALIZABLE_STRINGS_FILENAME = "Localizable.strings".freeze
7
- LOCALIZABLE_STRINGSDICT_FILENAME = "Localizable.stringsdict".freeze
8
- LOCALE_DIRECTORY_CONVENTION = "%{locale}.lproj".freeze
9
-
10
- def initialize
11
- @info_plist_serializer = Serializers::InfoPlistSerializer.new
12
- @localizable_strings_serializer = Serializers::LocalizableStringsSerializer.new
13
- @localizable_stringsdict_serializer = Serializers::LocalizableStringsdictSerializer.new
14
- @file_system_repository = Repositories::FileSystemRepository.new
15
- end
16
-
17
- def call(wording:, locale:, platform_dir:)
18
- LOGGER.debug("Starting export iOS wording for locale #{locale}")
19
- locale_wording = wording.translations_for(locale: locale)
20
- return unless has_ios_wording?(locale_wording: locale_wording)
21
-
22
- output_dir = platform_dir.join(LOCALE_DIRECTORY_CONVENTION % { locale: locale })
23
- @file_system_repository.create_directory(path: output_dir)
24
-
25
- export_info_plist(locale_wording: locale_wording, output_dir: output_dir)
26
- export_localizable_strings(locale_wording: locale_wording, output_dir: output_dir)
27
- export_localizable_stringsdict(locale_wording: locale_wording, output_dir: output_dir)
28
- end
29
-
30
- def should_export_locale_by_locale?
31
- true
32
- end
33
-
34
- private
35
-
36
- def has_ios_wording?(locale_wording:)
37
- locale_wording.has_info_plist_keys? ||
38
- locale_wording.has_singular_keys? ||
39
- locale_wording.has_plural_keys? ||
40
- locale_wording.has_adaptive_keys?
41
- end
42
-
43
- def export_info_plist(locale_wording:, output_dir:)
44
- return unless locale_wording.has_info_plist_keys?
45
- content = @info_plist_serializer.render(locale_wording: locale_wording)
46
- @file_system_repository.write(content: content, path: output_dir.join(INFO_PLIST_FILENAME))
47
- LOGGER.debug("#{INFO_PLIST_FILENAME} done !")
48
- end
49
-
50
- def export_localizable_strings(locale_wording:, output_dir:)
51
- return unless locale_wording.has_singular_keys?
52
- content = @localizable_strings_serializer.render(locale_wording: locale_wording)
53
- @file_system_repository.write(content: content, path: output_dir.join(LOCALIZABLE_STRINGS_FILENAME))
54
- LOGGER.debug("#{LOCALIZABLE_STRINGS_FILENAME} done !")
55
- end
56
-
57
- def export_localizable_stringsdict(locale_wording:, output_dir:)
58
- return unless locale_wording.has_plural_keys? || locale_wording.has_adaptive_keys?
59
- content = @localizable_stringsdict_serializer.render(locale_wording: locale_wording)
60
- @file_system_repository.write(content: content, path: output_dir.join(LOCALIZABLE_STRINGSDICT_FILENAME))
61
- LOGGER.debug("#{LOCALIZABLE_STRINGSDICT_FILENAME} done !")
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,27 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportJSONLocaleWording
5
- def initialize
6
- @json_serializer = Serializers::JSONSerializer.new
7
- @file_system_repository = Repositories::FileSystemRepository.new
8
- end
9
-
10
- def call(wording:, locale:, platform_dir:)
11
- LOGGER.debug("Starting export JSON wording for locale #{locale}")
12
- locale_wording = wording.translations_for(locale: locale)
13
- content = @json_serializer.render(locale_wording: locale_wording)
14
- return if content[locale].blank?
15
-
16
- @file_system_repository.create_directory(path: platform_dir)
17
- @file_system_repository.write(content: content, path: platform_dir.join("#{locale}.json"))
18
- LOGGER.debug("#{locale}.json done !")
19
- end
20
-
21
- def should_export_locale_by_locale?
22
- true
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,50 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportPlatformFactory
5
- def build(platform:)
6
- case platform
7
- when 'json'
8
- json_builder
9
- when 'yml'
10
- yaml_builder
11
- when 'android'
12
- android_builder
13
- when 'ios'
14
- ios_builder
15
- when 'properties'
16
- properties_builder
17
- when 'csv'
18
- csv_builder
19
- else
20
- raise ArgumentError.new('Unknown platform for builder factory')
21
- end
22
- end
23
-
24
- def json_builder
25
- @json_builder ||= ExportJSONLocaleWording.new
26
- end
27
-
28
- def yaml_builder
29
- @yaml_builder ||= ExportYAMLLocaleWording.new
30
- end
31
-
32
- def android_builder
33
- @android_builder ||= ExportAndroidLocaleWording.new
34
- end
35
-
36
- def ios_builder
37
- @ios_builder ||= ExportIOSLocaleWording.new
38
- end
39
-
40
- def properties_builder
41
- @properties_builder ||= ExportPropertiesLocaleWording.new
42
- end
43
-
44
- def csv_builder
45
- @csv_builder ||= ExportCSVLocaleWording.new
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,33 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportPropertiesLocaleWording
5
- def initialize
6
- @properties_serializer = Serializers::PropertiesSerializer.new
7
- @file_system_repository = Repositories::FileSystemRepository.new
8
- end
9
-
10
- def call(wording:, locale:, platform_dir:)
11
- LOGGER.debug("Starting export Properties wording for locale #{locale}")
12
- locale_wording = wording.translations_for(locale: locale)
13
- return unless has_properties_wording?(locale_wording: locale_wording)
14
-
15
- content = @properties_serializer.render(locale_wording: locale_wording)
16
- @file_system_repository.create_directory(path: platform_dir)
17
- @file_system_repository.write(content: content, path: platform_dir.join("#{locale}.properties"))
18
- LOGGER.debug("#{locale}.properties done !")
19
- end
20
-
21
- def should_export_locale_by_locale?
22
- true
23
- end
24
-
25
- private
26
-
27
- def has_properties_wording?(locale_wording:)
28
- locale_wording.has_singular_keys?
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,27 +0,0 @@
1
- module AdLocalize
2
- module Interactors
3
- module Platforms
4
- class ExportYAMLLocaleWording
5
- def initialize
6
- @yaml_serializer = Serializers::YAMLSerializer.new
7
- @file_system_repository = Repositories::FileSystemRepository.new
8
- end
9
-
10
- def call(wording:, locale:, platform_dir:)
11
- LOGGER.debug("Starting export YAML wording for locale #{locale}")
12
- locale_wording = wording.translations_for(locale: locale)
13
- content = @yaml_serializer.render(locale_wording:locale_wording)
14
- return if content[locale].blank?
15
-
16
- @file_system_repository.create_directory(path: platform_dir)
17
- @file_system_repository.write(content: content, path: platform_dir.join("#{locale}.yml"))
18
- LOGGER.debug("#{locale}.yml done !")
19
- end
20
-
21
- def should_export_locale_by_locale?
22
- true
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,73 +0,0 @@
1
- module AdLocalize
2
- module Mappers
3
- class CSVPathToWording
4
- def map(csv_path:)
5
- @headers = CSV.foreach(csv_path).first
6
- return unless valid?(csv_path: csv_path)
7
- translations = []
8
- validator = Validators::KeyValidator.new
9
-
10
- CSV.foreach(csv_path, headers: true, skip_blanks: true) do |row|
11
- row_translations = map_row(row: row, locales: locales)
12
- next if row_translations.blank?
13
-
14
- current_key = row_translations.first.key
15
- next if validator.has_warnings?(current_key)
16
-
17
- translations.concat(row_translations)
18
- end
19
-
20
- locale_wordings = translations.group_by(&:locale).map do |locale, group|
21
- Entities::LocaleWording.new(locale: locale, translations: group)
22
- end
23
- Entities::Wording.new(locale_wordings: locale_wordings, default_locale: locales.first)
24
- end
25
-
26
- private
27
-
28
- def valid?(csv_path:)
29
- File.exist?(csv_path) && has_key_column? && has_locales?
30
- end
31
-
32
- def locales
33
- @locales ||= compute_locales
34
- end
35
-
36
- def compute_locales
37
- return [] unless has_key_column? && key_column_index < @headers.size.pred
38
-
39
- @headers.slice(key_column_index.succ..-1).compact.reject do |header|
40
- header.to_s.include?(Constant::COMMENT_KEY_COLUMN_IDENTIFIER)
41
- end
42
- end
43
-
44
- def has_locales?
45
- locales.present?
46
- end
47
-
48
- def has_key_column?
49
- key_column_index.present?
50
- end
51
-
52
- def key_column_index
53
- @key_column_index ||= @headers.index(Constant::CSV_WORDING_KEYS_COLUMN)
54
- end
55
-
56
- def map_row(row:, locales:)
57
- csv_wording_key = row.field(Constant::CSV_WORDING_KEYS_COLUMN)
58
- return if csv_wording_key.blank?
59
-
60
- key = Entities::Key.new(label: csv_wording_key)
61
- locales.map do |locale|
62
- comment_column_name = "#{Constant::COMMENT_KEY_COLUMN_IDENTIFIER} #{locale}"
63
- Entities::Translation.new(
64
- locale: locale,
65
- key: key,
66
- value: row.field(locale),
67
- comment: row.field(comment_column_name)
68
- )
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,12 +0,0 @@
1
- module AdLocalize
2
- module Mappers
3
- class IOSTranslationMapper < TranslationMapper
4
- private
5
-
6
- def sanitize_value(value:)
7
- return if value.blank?
8
- value.gsub(/(?<!\\)\"/, "\\\"")
9
- end
10
- end
11
- end
12
- end
@@ -1,14 +0,0 @@
1
- module AdLocalize
2
- module Mappers
3
- class TranslationGroupMapper
4
- def initialize(translation_mapper: TranslationMapper.new)
5
- @translation_mapper = translation_mapper
6
- end
7
-
8
- def map(label:, translations:)
9
- translation_view_models = translations.map { |translation| @translation_mapper.map(translation: translation) }
10
- ViewModels::TranslationGroupViewModel.new(label: label, translation_view_models: translation_view_models)
11
- end
12
- end
13
- end
14
- end
@@ -1,30 +0,0 @@
1
- module AdLocalize
2
- module Mappers
3
- class TranslationMapper
4
- def map(translation:)
5
- ViewModels::TranslationViewModel.new(
6
- label: translation.key.label,
7
- key: key(translation: translation),
8
- value: sanitize_value(value: translation.value),
9
- comment: translation.comment
10
- )
11
- end
12
-
13
- protected
14
-
15
- def sanitize_value(value:)
16
- value
17
- end
18
-
19
- private
20
-
21
- def key(translation:)
22
- if translation.key.plural?
23
- translation.key.plural_key
24
- elsif translation.key.adaptive?
25
- translation.key.adaptive_key
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,67 +0,0 @@
1
- module AdLocalize
2
- module Mappers
3
- class ValueRangeToWording
4
- def map(value_range:)
5
- values = value_range.values
6
- analyze_header(first_row: values.first)
7
- return unless valid_header?
8
-
9
- translations = map_rows(values: values)
10
- locale_wordings = translations.group_by(&:locale).map do |locale, group|
11
- Entities::LocaleWording.new(locale: locale, translations: group)
12
- end
13
- Entities::Wording.new(locale_wordings: locale_wordings, default_locale: @locale_mapping.keys.first)
14
- end
15
-
16
- private
17
-
18
- def map_rows(values:)
19
- translations = []
20
- validator = Validators::KeyValidator.new
21
-
22
- values[1..-1].each do |row|
23
- row_translations = map_row(row: row)
24
- next if row_translations.blank?
25
-
26
- current_key = row_translations.first.key
27
- next if validator.has_warnings?(current_key)
28
-
29
- translations.concat(row_translations)
30
- end
31
- translations
32
- end
33
-
34
- def analyze_header(first_row:)
35
- @header = first_row
36
- @key_index = first_row.index(Constant::CSV_WORDING_KEYS_COLUMN)
37
- @locale_mapping = {}
38
- first_row[@key_index.succ..-1].each_index do |relative_index|
39
- absolute_index = @key_index.succ + relative_index
40
- next if first_row[absolute_index].blank? || first_row[absolute_index].include?(Constant::COMMENT_KEY_COLUMN_IDENTIFIER)
41
- @locale_mapping[first_row[absolute_index]] = { key_index: absolute_index }
42
- comment_column_name = "#{Constant::COMMENT_KEY_COLUMN_IDENTIFIER} #{first_row[absolute_index]}"
43
- @locale_mapping[first_row[absolute_index]][:comment_index] = first_row.index(comment_column_name)
44
- end
45
- end
46
-
47
- def map_row(row:)
48
- csv_wording_key = row[@key_index]
49
- return if csv_wording_key.blank?
50
- key = Entities::Key.new(label: csv_wording_key)
51
- @locale_mapping.map do |locale, index_mapping|
52
- comment = index_mapping[:comment_index].nil? ? nil : row[index_mapping[:comment_index]]
53
- Entities::Translation.new(
54
- locale: locale,
55
- key: key,
56
- value: row[index_mapping[:key_index]],
57
- comment: comment
58
- )
59
- end
60
- end
61
-
62
- def valid_header?
63
- @key_index.present? || @locale_mapping.keys.size.positive?
64
- end
65
- end
66
- end
67
- end
@@ -1,44 +0,0 @@
1
- module AdLocalize
2
- module Repositories
3
- class GSheetsRepository
4
- SCOPES = [Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY]
5
- ROWS_MAJOR_DIMENSION = 'ROWS'.freeze
6
- SPREADSHEET_APPLICATION_NAME = 'ad_localize'.freeze
7
-
8
- def initialize
9
- @service = Google::Apis::SheetsV4::SheetsService.new
10
- @service.client_options.application_name = SPREADSHEET_APPLICATION_NAME
11
- end
12
-
13
- def get_sheets_values(g_spreadsheet_options:)
14
- configure(json_configuration: g_spreadsheet_options.service_account_config) if @service.authorization.nil?
15
- spreadsheet = @service.get_spreadsheet(g_spreadsheet_options.spreadsheet_id)
16
- if g_spreadsheet_options.export_all
17
- spreadsheet.sheets.map { |sheet| get_sheet_values(spreadsheet_id: g_spreadsheet_options.spreadsheet_id, sheet: sheet) }
18
- elsif g_spreadsheet_options.sheet_ids.nil? || g_spreadsheet_options.sheet_ids.size.zero?
19
- spreadsheet.sheets[0..0].map { |sheet| get_sheet_values(spreadsheet_id: g_spreadsheet_options.spreadsheet_id, sheet: sheet) }
20
- else
21
- spreadsheet.sheets.select do |sheet|
22
- g_spreadsheet_options.sheet_ids.include?(sheet.properties.sheet_id.to_s)
23
- end.map do |sheet|
24
- get_sheet_values(spreadsheet_id: g_spreadsheet_options.spreadsheet_id, sheet: sheet)
25
- end
26
- end
27
- end
28
-
29
- private
30
-
31
- def configure(json_configuration:)
32
- raise ArgumentError.new('No service account configuration') if json_configuration.blank?
33
- json_key_io = StringIO.new(json_configuration)
34
- authorizer = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: json_key_io, scope: SCOPES)
35
- @service.authorization = authorizer
36
- end
37
-
38
- def get_sheet_values(spreadsheet_id:, sheet:)
39
- range = sheet.properties.title
40
- @service.get_spreadsheet_values(spreadsheet_id, range, major_dimension: ROWS_MAJOR_DIMENSION)
41
- end
42
- end
43
- end
44
- end