aigu 0.4.5 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84521cb2e0c9827217bada8bda9ec4a7c5a0cfe4
4
- data.tar.gz: 8142e1fc307e0568a3115ffe10d8a6be31160836
3
+ metadata.gz: 2541e277f5d4bb08688838d37d097f32567a2682
4
+ data.tar.gz: af50d9f37a958dfd01306b5206434334d3feb46a
5
5
  SHA512:
6
- metadata.gz: 372b73941b8fa6c84f8e5914ba438f612e3e221445048510273bb399482d9cff2917de2f976cd6d4b700ab5f58cee1b5970e9bda866ed4d867624c5415365ab7
7
- data.tar.gz: 39a2099251184eb6ab56a384f23fa7fb4a4585cc216e439859c9a7fc84877567fb743aba2ae7a815d81789bba651bde7022e78da1ebc27f4b8e0dce0e75c5106
6
+ metadata.gz: 2bd8d227116755e4eeecacfd2cd91558cfa7c13881804acfa261cd0ec66454902e71d1fd1bf4ac093749be64c4a50e27888323802d2ea18100e39c758c6aaa45
7
+ data.tar.gz: 3ecec7944c56ef8c42180db1033ed45489840610b5a27589fbe17d5a5cd77f6fc2b19129bcb50c62e998873cfcdd2c206cd856cfef0d1cdd902407031c09186e
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2
5
+
6
+ sudo: false
7
+
8
+ script:
9
+ - 'echo "Checking code style" && ./bin/phare'
10
+ - 'echo "Running specs" && bundle exec rake spec'
data/README.md CHANGED
@@ -4,8 +4,8 @@ Aigu is a set of utility to process localization files to genarate JSON files
4
4
  to push to Accent service. Pulling from Accent to update localization files
5
5
  is also supported.
6
6
 
7
- The following localization file formats are supported: Rails YAML, Android
8
- XML, Java Enum Core, iOS strings and stringsdict.
7
+ The following localization file formats are supported: Rails YAML, Android XML,
8
+ Java Enum Core, iOS strings and stringsdict and Ember JavaScript.
9
9
 
10
10
  Merge and unmerge commands allows to merge different source files into a
11
11
  single JSON file to send as a single Accent project.
@@ -28,36 +28,37 @@ Aigu provides an executable named `aigu`.
28
28
 
29
29
  ### Exporting the JSON file for Accent
30
30
 
31
- The `export` command looks for YAML localization files and generates a JSON
32
- file. This file will then be compatible with Accent.
31
+ The `export` command looks for localization files and generates a JSON file.
32
+ This file will then be compatible with Accent.
33
33
 
34
34
  ```bash
35
- $ aigu export --locale=fr --input-directory=config/locales --output-file=my-project.json
35
+ $ aigu <export-command> --locale=fr --input-directory=config/locales --output-file=my-project.json
36
36
  ```
37
37
 
38
38
  #### Options
39
39
 
40
- | Option | Description |
41
- |-------------------|---------------------------------------------------------------------------------------------------------------------------|
42
- | `locale` | The locale used to find localization files. Files that match `*.<locale>.yml` in the input directory will be processed. |
43
- | `input-directory` | The directory used to find localization files |
44
- | `output-file` | The path to the JSON file that will be written by `aigu` |
45
- | `ignore` | The patterns `aigu` will use to skip ignored files (eg. `routes.yml`)
40
+ | Option | Description |
41
+ |-------------------|------------------------------------------------------------------------|
42
+ | `locale` | The locale used to find localization files. |
43
+ | `input-directory` | The directory used to find localization files |
44
+ | `output-file` | The path to the JSON file that will be written by `aigu` |
45
+ | `ignore` | The patterns `aigu` will use to skip ignored files (eg. `routes.yml`) |
46
46
 
47
- | Command | File format |
47
+ | Command | File format |
48
48
  |-------------------|-------------------------------------------------------|
49
- | `export` | Rails YAML |
50
- | `andoird_export` | Android XML |
51
- | `core_export` | Java Enum Core |
52
- | `ios_export` | iOS strings & stringsdict |
49
+ | `rails-export` | Rails YAML |
50
+ | `android-export` | Android XML |
51
+ | `core-export` | Java Enum Core |
52
+ | `ios-export` | iOS strings & stringsdict |
53
+ | `ember-export` | Ember.js JavaScript (`node` and `babel` are required) |
53
54
 
54
55
  ### Importing the JSON file from Accent
55
56
 
56
57
  The `import` command takes a generated JSON file from Accent and generates
57
- the original YAML file structure.
58
+ localization files in the output directory.
58
59
 
59
60
  ```bash
60
- $ aigu import --locale=fr --input-file=file-from-accent.json --output-directory=config/locales
61
+ $ aigu <import-command> --locale=fr --input-file=file-from-accent.json --output-directory=config/locales
61
62
  ```
62
63
 
63
64
  #### Options
@@ -68,12 +69,12 @@ $ aigu import --locale=fr --input-file=file-from-accent.json --output-directory=
68
69
  | `input-file` | The path to the Accent-generated JSON file |
69
70
  | `output-directory` | The directory where the localization YAML files will be generated |
70
71
 
71
- | Command | File format |
72
- |-------------------|-------------------------------------------------------|
73
- | `import` | Rails YAML |
74
- | `andoird_import` | Android XML |
75
- | `core_import` | Java Enum Core |
76
- | `ios_import` | iOS strings & stringsdict |
72
+ | Command | File format |
73
+ |-------------------|----------------------------|
74
+ | `rails-import` | Rails YAML |
75
+ | `android-import` | Android XML |
76
+ | `core-import` | Java Enum Core |
77
+ | `ios-import` | iOS strings & stringsdict |
77
78
 
78
79
  ### Merge
79
80
 
@@ -107,11 +108,11 @@ The `push` command takes a JSON file exported by Aigu and send it to Accent
107
108
 
108
109
  #### Options
109
110
 
110
- | Option | Description |
111
- |--------------------|---------------------------------------------------------------------------------------------------|
112
- | `input-file` | The path to the Aigu-generated JSON file |
113
- | `accent-api-key` | The API key of the Accent project. (See project config in Accent) |
114
- | `accent-url` | The URL to the instance of Accent to use (Prod: http://accent.mirego.com, QA: https://mirego-accent-qa.herokuapp.com ) |
111
+ | Option | Description |
112
+ |--------------------|-------------------------------------------------------------------|
113
+ | `input-file` | The path to the Aigu-generated JSON file |
114
+ | `accent-api-key` | The API key of the Accent project. (See project config in Accent) |
115
+ | `accent-url` | The URL to the instance of Accent to use |
115
116
 
116
117
  ### Pulling from Accent
117
118
 
@@ -119,11 +120,11 @@ The `pull` command GET the Accent generated JSON file
119
120
 
120
121
  #### Options
121
122
 
122
- | Option | Description |
123
- |--------------------|---------------------------------------------------------------------------------------------------|
124
- | `output-file` | The path to write the Accent JSON file to |
125
- | `accent-api-key` | The API key of the Accent project. (See project config in Accent) |
126
- | `accent-url` | The URL to the instance of Accent to use (Prod: http://accent.mirego.com, QA: https://mirego-accent-qa.herokuapp.com ) |
123
+ | Option | Description |
124
+ |--------------------|-------------------------------------------------------------------|
125
+ | `output-file` | The path to write the Accent JSON file to |
126
+ | `accent-api-key` | The API key of the Accent project. (See project config in Accent) |
127
+ | `accent-url` | The URL to the instance of Accent to use |
127
128
 
128
129
  ### Using `.aigu.yml`
129
130
 
@@ -153,7 +154,7 @@ the commit if there are errors.
153
154
 
154
155
  ## License
155
156
 
156
- `Aigu` is © 2014 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/aigu/blob/master/LICENSE.md) file.
157
+ `Aigu` is © 2014-2015 [Mirego](http://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/aigu/blob/master/LICENSE.md) file.
157
158
 
158
159
  ## About Mirego
159
160
 
@@ -11,16 +11,22 @@ require 'openssl'
11
11
 
12
12
  require 'aigu/extensions/hash'
13
13
  require 'aigu/cli'
14
+
14
15
  require 'aigu/importer'
15
16
  require 'aigu/exporter'
17
+ require 'aigu/rails_importer'
18
+ require 'aigu/rails_exporter'
16
19
  require 'aigu/android_exporter'
17
20
  require 'aigu/android_importer'
18
21
  require 'aigu/core_exporter'
19
22
  require 'aigu/core_importer'
20
23
  require 'aigu/ios_exporter'
21
24
  require 'aigu/ios_importer'
22
- require 'aigu/unmergeer'
23
- require 'aigu/mergeer'
25
+ require 'aigu/ember_exporter'
26
+ require 'aigu/ember_importer'
27
+
28
+ require 'aigu/unmerger'
29
+ require 'aigu/merger'
24
30
  require 'aigu/puller'
25
31
  require 'aigu/pusher'
26
32
 
@@ -1,29 +1,5 @@
1
1
  module Aigu
2
- class AndroidExporter
3
- def initialize(opts = {})
4
- @output_file = opts[:'output-file']
5
- @input_directory = opts[:'input-directory']
6
- @locale = opts[:locale]
7
- @ignore = opts[:ignore]
8
- end
9
-
10
- def process!
11
- puts "Generating Accent JSON file `#{@output_file}` based on Android resources files in `#{@input_directory}` directory"
12
-
13
- if @ignore
14
- print 'Ignoring '
15
- puts @ignore.join(', ')
16
- end
17
-
18
- puts '---'
19
-
20
- build_output
21
- write_json_file
22
-
23
- puts '---'
24
- puts 'Done'
25
- end
26
-
2
+ class AndroidExporter < Exporter
27
3
  protected
28
4
 
29
5
  def build_output
@@ -1,14 +1,8 @@
1
1
  module Aigu
2
- class AndroidImporter
2
+ class AndroidImporter < Importer
3
3
  ARRAY_REGEX = /__ARRAY_ITEM__#(?<index>\d+)$/
4
4
  TYPE_REGEX = /__@TYPE_(?<type_key>.+)$/
5
5
 
6
- def initialize(opts = {})
7
- @input_file = opts[:'input-file']
8
- @output_directory = opts[:'output-directory']
9
- @locale = opts[:locale]
10
- end
11
-
12
6
  def process!
13
7
  puts "Generating Android XML files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
14
8
  puts '---'
@@ -1,8 +1,24 @@
1
1
  module Aigu
2
2
  class CLI
3
+ COMMANDS = {
4
+ 'rails-import' => 'RailsImporter',
5
+ 'rails-export' => 'RailsExporter',
6
+ 'android-import' => 'AndroidImporter',
7
+ 'android-export' => 'AndroidExporter',
8
+ 'core-import' => 'CoreImporter',
9
+ 'core-export' => 'CoreExporter',
10
+ 'ios-import' => 'IOSImporter',
11
+ 'ios-export' => 'IOSExporter',
12
+ 'ember-import' => 'EmberImporter',
13
+ 'ember-export' => 'EmberExporter',
14
+ 'merge' => 'Merger',
15
+ 'unmerge' => 'Unmerger'
16
+ }
17
+
3
18
  def initialize(env, argv)
4
19
  @env = env
5
20
  @command = argv.first =~ /^[^-]/ ? argv.shift : nil
21
+ @command = @command.gsub('_', '-') if @command
6
22
  @argv = argv
7
23
  @options = {}
8
24
  @options = parse_options_from_yaml(@options)
@@ -10,12 +26,10 @@ module Aigu
10
26
  end
11
27
 
12
28
  def run
13
- service_name = "#{@command}er".split('_').map(&:capitalize).join
14
-
15
- begin
16
- service_class = Aigu.const_get(service_name)
17
- rescue NameError
18
- puts "The Aigu::#{service_name} service doesn’t exist. Nice try."
29
+ if COMMANDS[@command]
30
+ service_class = Aigu.const_get(COMMANDS[@command])
31
+ else
32
+ puts "The #{@command} command doesn’t exist. Nice try."
19
33
  exit 0
20
34
  end
21
35
 
@@ -42,7 +56,14 @@ module Aigu
42
56
  # rubocop:disable Metrics/MethodLength
43
57
  def parse_options_from_arguments(options)
44
58
  OptionParser.new do |opts|
45
- opts.banner = 'Usage: aigu [options]'
59
+ opts.banner = <<DOC
60
+ "Usage: aigu <command> [options]"
61
+
62
+ Commands:
63
+ #{COMMANDS.keys.map { |command| " #{command}" }.join("\n")}
64
+
65
+ Options:
66
+ DOC
46
67
 
47
68
  opts.on('--input-directory=', 'The directory in which the Rails YAML localization files are stored.') do |directory|
48
69
  options[:'input-directory'] = directory
@@ -1,31 +1,7 @@
1
1
  module Aigu
2
- class CoreExporter
2
+ class CoreExporter < Exporter
3
3
  ENUM_LINE_REGEX = /^\s*(?<key>\w+)\s?\("(?<value_en>.*)",\s?"(?<value_fr>.*)"\)[,;]?\s$/
4
4
 
5
- def initialize(opts = {})
6
- @output_file = opts[:'output-file']
7
- @input_directory = opts[:'input-directory']
8
- @locale = opts[:locale]
9
- @ignore = opts[:ignore]
10
- end
11
-
12
- def process!
13
- puts "Generating Accent JSON file `#{@output_file}` based on Core Java Enum files in `#{@input_directory}` directory"
14
-
15
- if @ignore
16
- print 'Ignoring '
17
- puts @ignore.join(', ')
18
- end
19
-
20
- puts '---'
21
-
22
- build_output
23
- write_json_file
24
-
25
- puts '---'
26
- puts 'Done'
27
- end
28
-
29
5
  protected
30
6
 
31
7
  def build_output
@@ -1,13 +1,7 @@
1
1
  module Aigu
2
- class CoreImporter
2
+ class CoreImporter < Importer
3
3
  ENUM_LINE_REGEX = /^\s*(?<key>\w+)\s?\("(?<value_en>.*)",\s?"(?<value_fr>.*)"\)(?<comma>[,;]?)\s$/
4
4
 
5
- def initialize(opts = {})
6
- @input_file = opts[:'input-file']
7
- @output_directory = opts[:'output-directory']
8
- @locale = opts[:locale]
9
- end
10
-
11
5
  def process!
12
6
  puts "Updating Core Java Enum files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
13
7
  puts '---'
@@ -0,0 +1,66 @@
1
+ module Aigu
2
+ class EmberExporter < Exporter
3
+ protected
4
+
5
+ def build_output
6
+ @output = {}
7
+
8
+ pattern = File.join(@input_directory, "#{@locale}/translations.js")
9
+ Dir[pattern].each do |file|
10
+ filepath = file.gsub(/\A#{@input_directory}\//, '')
11
+
12
+ if ignored_filepath?(filepath)
13
+ puts "Ignoring #{filepath}"
14
+ else
15
+ puts "Processing #{filepath}"
16
+
17
+ content = JSON.parse(run_javascript_script(file))
18
+
19
+ content = flattenize_hash(content)
20
+ @output.merge! content
21
+ end
22
+ end
23
+ end
24
+
25
+ def run_javascript_script(file)
26
+ script = "require('babel/register')({stage: 0}); console.log(JSON.stringify(require('#{file}')))"
27
+ command = "node -e \"#{script}\" 2>/dev/null"
28
+ puts "Executing `#{command}`"
29
+ content = `#{command}`
30
+
31
+ if content.empty?
32
+ puts 'There was an error when executing the command. Please make sure `node` and `babel` are installed.'
33
+ exit 0
34
+ end
35
+
36
+ content
37
+ end
38
+
39
+ def write_json_file
40
+ file_path = @output_file
41
+ puts "Generating #{file_path}"
42
+ FileUtils.mkdir_p(File.dirname(file_path))
43
+
44
+ File.open(file_path, 'w+') do |file|
45
+ file << @output.to_json
46
+ end
47
+ end
48
+
49
+ def flattenize_hash(hash, base_key = '')
50
+ if hash.is_a?(Hash)
51
+ hash.reduce({}) do |memo, (key, value)|
52
+ new_base_key = [base_key, key].join('.').gsub(/\|\.+/, '|')
53
+ memo.merge! flattenize_hash(value, new_base_key)
54
+ end
55
+ else
56
+ { base_key.gsub(/^\./, '') => hash }
57
+ end
58
+ end
59
+
60
+ def ignored_filepath?(filepath)
61
+ @ignore && @ignore.any? do |pattern|
62
+ File.fnmatch(pattern, filepath, File::FNM_PATHNAME | File::FNM_DOTMATCH)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,54 @@
1
+ module Aigu
2
+ class EmberImporter < Importer
3
+ def process!
4
+ puts "Generating JavaScript file in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
5
+ puts '---'
6
+
7
+ parse_json
8
+ build_blob
9
+ write_javascript_file
10
+
11
+ puts '---'
12
+ puts 'Done'
13
+ end
14
+
15
+ protected
16
+
17
+ def parse_json
18
+ json = File.read(@input_file)
19
+ @object = JSON.parse(json)
20
+ end
21
+
22
+ def write_javascript_file
23
+ file_path = File.join(@output_directory, "#{@locale}/translations.js")
24
+ puts "Generating #{file_path}"
25
+ FileUtils.mkdir_p(File.dirname(file_path))
26
+
27
+ javascript_object = JSON.pretty_generate(@blob)
28
+ content = "export default #{javascript_object};\n"
29
+
30
+ File.open(file_path, 'w+') do |file|
31
+ file << content
32
+ end
33
+ end
34
+
35
+ def build_blob
36
+ @blob = Hash.recursive
37
+
38
+ @object.each_pair do |key, value|
39
+ parts = key.split('.')
40
+ hash = @blob[parts.first]
41
+
42
+ parts.each_with_index do |part, index|
43
+ next if index.zero?
44
+
45
+ if index + 1 < parts.length
46
+ hash = hash[part]
47
+ else
48
+ hash[part] = value
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -8,7 +8,7 @@ module Aigu
8
8
  end
9
9
 
10
10
  def process!
11
- puts "Generating Accent JSON file `#{@output_file}` based on YAML localization files in `#{@input_directory}` directory"
11
+ puts "Generating Accent JSON file `#{@output_file}` based on localization files in `#{@input_directory}` directory"
12
12
 
13
13
  if @ignore
14
14
  print 'Ignoring '
@@ -23,89 +23,5 @@ module Aigu
23
23
  puts '---'
24
24
  puts 'Done'
25
25
  end
26
-
27
- protected
28
-
29
- def build_output
30
- @output = {}
31
-
32
- pattern = File.join(@input_directory, '**', "*.#{@locale}.yml")
33
- Dir[pattern].each do |file|
34
- filepath = file.gsub(/\A#{@input_directory}\//, '')
35
-
36
- if ignored_filepath?(filepath)
37
- puts "Ignoring #{filepath}"
38
- else
39
- puts "Processing #{filepath}"
40
- content = YAML.load_file(file)
41
- base_key = file.gsub(@input_directory, '').gsub(/^\/*/, '').gsub(/\.yml$/, '|')
42
-
43
- content = flattenize_hash(content, base_key)
44
- content = flattenize_content_values(content)
45
- content = globalize_content_keys(content)
46
- @output.merge! content
47
- end
48
- end
49
- end
50
-
51
- def write_json_file
52
- file_path = @output_file
53
- puts "Generating #{file_path}"
54
- FileUtils.mkdir_p(File.dirname(file_path))
55
-
56
- File.open(file_path, 'w+') do |file|
57
- file << @output.to_json
58
- end
59
- end
60
-
61
- def globalize_content_keys(content)
62
- content.reduce({}) do |memo, (key, value)|
63
- globalized_key = key.gsub(/\.#{@locale}\|#{@locale}\./, '|')
64
- memo.merge globalized_key => value
65
- end
66
- end
67
-
68
- def flattenize_content_values(hash)
69
- hash.each_with_object({}) do |(key, value), memo|
70
- if value.is_a?(Array)
71
- value.each_with_index do |array_value, index|
72
- tainted_key = "#{key}___KEY___#{index}"
73
- memo[tainted_key] = sanitize_value_to_string(array_value)
74
- end
75
- else
76
- memo[key] = sanitize_value_to_string(value)
77
- end
78
- end
79
- end
80
-
81
- def sanitize_value_to_string(value)
82
- case value
83
- when true
84
- '___TRUE___'
85
- when false
86
- '___FALSE___'
87
- when nil
88
- '___NULL___'
89
- else
90
- value
91
- end
92
- end
93
-
94
- def flattenize_hash(hash, base_key = '')
95
- if hash.is_a?(Hash)
96
- hash.reduce({}) do |memo, (key, value)|
97
- new_base_key = [base_key, key].join('.').gsub(/\|\.+/, '|')
98
- memo.merge! flattenize_hash(value, new_base_key)
99
- end
100
- else
101
- { base_key => hash }
102
- end
103
- end
104
-
105
- def ignored_filepath?(filepath)
106
- @ignore && @ignore.any? do |pattern|
107
- File.fnmatch(pattern, filepath, File::FNM_PATHNAME | File::FNM_DOTMATCH)
108
- end
109
- end
110
26
  end
111
27
  end
@@ -1,98 +1,7 @@
1
- module Aigu
2
- class Importer
3
- ARRAY_REGEX = /___KEY___(?<index>\d+)$/
4
-
5
- def initialize(opts = {})
6
- @input_file = opts[:'input-file']
7
- @output_directory = opts[:'output-directory']
8
- @locale = opts[:locale]
9
- end
10
-
11
- def process!
12
- puts "Generating YAML files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
13
- puts '---'
14
-
15
- parse_json
16
- build_blob
17
- write_yaml_files
18
-
19
- puts '---'
20
- puts 'Done'
21
- end
22
-
23
- protected
24
-
25
- def parse_json
26
- json = File.read(@input_file)
27
- @object = JSON.parse(json)
28
- @object = expand_content_values(@object)
29
- @object = localize_content_keys(@object)
30
- end
31
-
32
- def write_yaml_files
33
- @blob.each_pair do |file_name, hash|
34
- file_path = File.join(@output_directory, "#{file_name}.yml")
35
- puts "Generating #{file_path}"
36
- FileUtils.mkdir_p(File.dirname(file_path))
37
-
38
- File.open(file_path, 'w+') do |file|
39
- file << hash.to_yaml(line_width: 100_000_000)
40
- end
41
- end
42
- end
43
-
44
- def build_blob
45
- @blob = Hash.recursive
46
-
47
- @object.each_pair do |key, value|
48
- filename, flat_key = key.split('|')
49
-
50
- parts = flat_key.split('.')
51
- hash = @blob[filename]
52
-
53
- parts.each_with_index do |part, index|
54
- if index + 1 < parts.length
55
- hash = hash[part]
56
- else
57
- hash[part] = value
58
- end
59
- end
60
- end
61
- end
62
-
63
- def localize_content_keys(content)
64
- content.reduce({}) do |memo, (key, value)|
65
- localized_key = key.gsub(/^([^|]+)\|/, "\\1.#{@locale}|#{@locale}.")
66
- memo.merge localized_key => value
67
- end
68
- end
69
-
70
- def expand_content_values(content)
71
- content.each_with_object({}) do |(key, value), memo|
72
- match_data = key.match(ARRAY_REGEX)
73
-
74
- if match_data
75
- canonical_key = key.gsub(ARRAY_REGEX, '')
76
- value_index = match_data[:index].to_i
77
- memo[canonical_key] ||= []
78
- memo[canonical_key][value_index] = sanitize_string_to_value(value)
79
- else
80
- memo[key] = sanitize_string_to_value(value)
81
- end
82
- end
83
- end
84
-
85
- def sanitize_string_to_value(string)
86
- case string
87
- when '___TRUE___'
88
- true
89
- when '___FALSE___'
90
- false
91
- when '___NULL___'
92
- nil
93
- else
94
- string
95
- end
96
- end
1
+ class Importer
2
+ def initialize(opts = {})
3
+ @input_file = opts[:'input-file']
4
+ @output_directory = opts[:'output-directory']
5
+ @locale = opts[:locale]
97
6
  end
98
7
  end
@@ -1,5 +1,5 @@
1
1
  module Aigu
2
- class IosExporter
2
+ class IOSExporter < Exporter
3
3
  PROP_LINE_REGEX = /^\s*"(?<key>.+)"\s?=\s?"(?<value>.*)";\s$/
4
4
 
5
5
  DICT_DICT_OPEN_REGEX = /^\s*<dict>\s*$/
@@ -7,30 +7,6 @@ module Aigu
7
7
  DICT_KEY_REGEX = /^\s*<key>(?<text>.*)<\/key>\s*$/
8
8
  DICT_STRING_REGEX = /^\s*<string>(?<text>.*)<\/string>\s*$/
9
9
 
10
- def initialize(opts = {})
11
- @output_file = opts[:'output-file']
12
- @input_directory = opts[:'input-directory']
13
- @locale = opts[:locale]
14
- @ignore = opts[:ignore]
15
- end
16
-
17
- def process!
18
- puts "Generating Accent JSON file `#{@output_file}` based on IOS strings files in `#{@input_directory}` directory"
19
-
20
- if @ignore
21
- print 'Ignoring '
22
- puts @ignore.join(', ')
23
- end
24
-
25
- puts '---'
26
-
27
- build_output
28
- write_json_file
29
-
30
- puts '---'
31
- puts 'Done'
32
- end
33
-
34
10
  protected
35
11
 
36
12
  def build_output
@@ -1,16 +1,10 @@
1
1
  module Aigu
2
- class IosImporter
2
+ class IOSImporter < Importer
3
3
  DICT_DICT_OPEN_REGEX = /^\s*<dict>\s*$/
4
4
  DICT_DICT_CLOSE_REGEX = /^\s*<\/dict>\s*$/
5
5
  DICT_KEY_REGEX = /^\s*<key>(?<text>.*)<\/key>\s*$/
6
6
  DICT_STRING_REGEX = /^(?<left>\s*<string>)(?<text>.*)(?<right><\/string>\s*)$/
7
7
 
8
- def initialize(opts = {})
9
- @input_file = opts[:'input-file']
10
- @output_directory = opts[:'output-directory']
11
- @locale = opts[:locale]
12
- end
13
-
14
8
  def process!
15
9
  puts "Generating IOS strings files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
16
10
  puts '---'
@@ -1,5 +1,5 @@
1
1
  module Aigu
2
- class Mergeer
2
+ class Merger
3
3
  def initialize(opts = {})
4
4
  @output_file = opts[:'output-file']
5
5
  @input_directory = opts[:'input-directory']
@@ -2,6 +2,10 @@ module Aigu
2
2
  class Pusher
3
3
  PUSH_PATH = '/public_api/revisions'
4
4
 
5
+ HTTP_ALREADY_UP_TO_DATE = '200'.freeze
6
+ HTTP_UPDATED = '201'.freeze
7
+ HTTP_SUCCESS_CODES = [HTTP_ALREADY_UP_TO_DATE, HTTP_UPDATED].freeze
8
+
5
9
  def initialize(opts = {})
6
10
  @input_file = opts[:'input-file']
7
11
  @accent_api_key = opts[:'accent-api-key']
@@ -40,7 +44,7 @@ module Aigu
40
44
 
41
45
  puts 'Response: ' + response.code
42
46
 
43
- if response.code != '201'
47
+ unless HTTP_SUCCESS_CODES.include? response.code
44
48
  puts 'Error pushing string update to Accent'
45
49
  exit 1
46
50
  end
@@ -0,0 +1,87 @@
1
+ module Aigu
2
+ class RailsExporter < Exporter
3
+ protected
4
+
5
+ def build_output
6
+ @output = {}
7
+
8
+ pattern = File.join(@input_directory, '**', "*.#{@locale}.yml")
9
+ Dir[pattern].each do |file|
10
+ filepath = file.gsub(/\A#{@input_directory}\//, '')
11
+
12
+ if ignored_filepath?(filepath)
13
+ puts "Ignoring #{filepath}"
14
+ else
15
+ puts "Processing #{filepath}"
16
+ content = YAML.load_file(file)
17
+ base_key = file.gsub(@input_directory, '').gsub(/^\/*/, '').gsub(/\.yml$/, '|')
18
+
19
+ content = flattenize_hash(content, base_key)
20
+ content = flattenize_content_values(content)
21
+ content = globalize_content_keys(content)
22
+ @output.merge! content
23
+ end
24
+ end
25
+ end
26
+
27
+ def write_json_file
28
+ file_path = @output_file
29
+ puts "Generating #{file_path}"
30
+ FileUtils.mkdir_p(File.dirname(file_path))
31
+
32
+ File.open(file_path, 'w+') do |file|
33
+ file << @output.to_json
34
+ end
35
+ end
36
+
37
+ def globalize_content_keys(content)
38
+ content.reduce({}) do |memo, (key, value)|
39
+ globalized_key = key.gsub(/\.#{@locale}\|#{@locale}\./, '|')
40
+ memo.merge globalized_key => value
41
+ end
42
+ end
43
+
44
+ def flattenize_content_values(hash)
45
+ hash.each_with_object({}) do |(key, value), memo|
46
+ if value.is_a?(Array)
47
+ value.each_with_index do |array_value, index|
48
+ tainted_key = "#{key}___KEY___#{index}"
49
+ memo[tainted_key] = sanitize_value_to_string(array_value)
50
+ end
51
+ else
52
+ memo[key] = sanitize_value_to_string(value)
53
+ end
54
+ end
55
+ end
56
+
57
+ def sanitize_value_to_string(value)
58
+ case value
59
+ when true
60
+ '___TRUE___'
61
+ when false
62
+ '___FALSE___'
63
+ when nil
64
+ '___NULL___'
65
+ else
66
+ value
67
+ end
68
+ end
69
+
70
+ def flattenize_hash(hash, base_key = '')
71
+ if hash.is_a?(Hash)
72
+ hash.reduce({}) do |memo, (key, value)|
73
+ new_base_key = [base_key, key].join('.').gsub(/\|\.+/, '|')
74
+ memo.merge! flattenize_hash(value, new_base_key)
75
+ end
76
+ else
77
+ { base_key => hash }
78
+ end
79
+ end
80
+
81
+ def ignored_filepath?(filepath)
82
+ @ignore && @ignore.any? do |pattern|
83
+ File.fnmatch(pattern, filepath, File::FNM_PATHNAME | File::FNM_DOTMATCH)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,92 @@
1
+ module Aigu
2
+ class RailsImporter < Importer
3
+ ARRAY_REGEX = /___KEY___(?<index>\d+)$/
4
+
5
+ def process!
6
+ puts "Generating YAML files in `#{@output_directory}` based on Accent-generated `#{@input_file}` file"
7
+ puts '---'
8
+
9
+ parse_json
10
+ build_blob
11
+ write_yaml_files
12
+
13
+ puts '---'
14
+ puts 'Done'
15
+ end
16
+
17
+ protected
18
+
19
+ def parse_json
20
+ json = File.read(@input_file)
21
+ @object = JSON.parse(json)
22
+ @object = expand_content_values(@object)
23
+ @object = localize_content_keys(@object)
24
+ end
25
+
26
+ def write_yaml_files
27
+ @blob.each_pair do |file_name, hash|
28
+ file_path = File.join(@output_directory, "#{file_name}.yml")
29
+ puts "Generating #{file_path}"
30
+ FileUtils.mkdir_p(File.dirname(file_path))
31
+
32
+ File.open(file_path, 'w+') do |file|
33
+ file << hash.to_yaml(line_width: 100_000_000)
34
+ end
35
+ end
36
+ end
37
+
38
+ def build_blob
39
+ @blob = Hash.recursive
40
+
41
+ @object.each_pair do |key, value|
42
+ filename, flat_key = key.split('|')
43
+
44
+ parts = flat_key.split('.')
45
+ hash = @blob[filename]
46
+
47
+ parts.each_with_index do |part, index|
48
+ if index + 1 < parts.length
49
+ hash = hash[part]
50
+ else
51
+ hash[part] = value
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def localize_content_keys(content)
58
+ content.reduce({}) do |memo, (key, value)|
59
+ localized_key = key.gsub(/^([^|]+)\|/, "\\1.#{@locale}|#{@locale}.")
60
+ memo.merge localized_key => value
61
+ end
62
+ end
63
+
64
+ def expand_content_values(content)
65
+ content.each_with_object({}) do |(key, value), memo|
66
+ match_data = key.match(ARRAY_REGEX)
67
+
68
+ if match_data
69
+ canonical_key = key.gsub(ARRAY_REGEX, '')
70
+ value_index = match_data[:index].to_i
71
+ memo[canonical_key] ||= []
72
+ memo[canonical_key][value_index] = sanitize_string_to_value(value)
73
+ else
74
+ memo[key] = sanitize_string_to_value(value)
75
+ end
76
+ end
77
+ end
78
+
79
+ def sanitize_string_to_value(string)
80
+ case string
81
+ when '___TRUE___'
82
+ true
83
+ when '___FALSE___'
84
+ false
85
+ when '___NULL___'
86
+ nil
87
+ else
88
+ string
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,5 +1,5 @@
1
1
  module Aigu
2
- class Unmergeer
2
+ class Unmerger
3
3
  UNMERGE_REGEX = /^@(?<file_name>\w+)@__(?<key>.+)$/
4
4
 
5
5
  def initialize(opts = {})
@@ -1,3 +1,3 @@
1
1
  module Aigu
2
- VERSION = '0.4.5'
2
+ VERSION = '0.5'
3
3
  end
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Aigu::IosExporter do
3
+ describe Aigu::IOSExporter do
4
4
  describe :parse_strings_file do
5
- let(:exporter) { Aigu::IosExporter.new }
5
+ let(:exporter) { Aigu::IOSExporter.new }
6
6
  let(:parse_strings_file) { exporter.send(:parse_strings_file, content) }
7
7
 
8
8
  let(:content) do
@@ -33,7 +33,7 @@ describe Aigu::IosExporter do
33
33
  end
34
34
 
35
35
  describe :parse_stringsdict_file do
36
- let(:exporter) { Aigu::IosExporter.new }
36
+ let(:exporter) { Aigu::IOSExporter.new }
37
37
  let(:parse_stringsdict_file) { exporter.send(:parse_stringsdict_file, content) }
38
38
 
39
39
  let(:content) do
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Aigu::IosImporter do
3
+ describe Aigu::IOSImporter do
4
4
  describe :format_strings_file do
5
- let(:importer) { Aigu::IosImporter.new }
5
+ let(:importer) { Aigu::IOSImporter.new }
6
6
  let(:format_strings_file) { importer.send(:format_strings_file, content) }
7
7
 
8
8
  let(:content) do
@@ -33,7 +33,7 @@ describe Aigu::IosImporter do
33
33
  end
34
34
 
35
35
  describe :update_stringsdict_content do
36
- let(:exporter) { Aigu::IosImporter.new }
36
+ let(:exporter) { Aigu::IOSImporter.new }
37
37
  let(:update_stringsdict_content) { exporter.send(:update_stringsdict_content, file_content, new_hash) }
38
38
 
39
39
  let(:file_content) do
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Aigu::Exporter do
3
+ describe Aigu::RailsExporter do
4
4
  describe :flattenize_content_values do
5
- let(:exporter) { Aigu::Exporter.new }
5
+ let(:exporter) { Aigu::RailsExporter.new }
6
6
  let(:flattenized_content) { exporter.send(:flattenize_content_values, content) }
7
7
 
8
8
  let(:content) do
@@ -37,7 +37,7 @@ describe Aigu::Exporter do
37
37
  end
38
38
 
39
39
  describe :globalize_content_keys do
40
- let(:exporter) { Aigu::Exporter.new }
40
+ let(:exporter) { Aigu::RailsExporter.new }
41
41
  let(:globalized_keys) { exporter.send(:globalize_content_keys, content).keys }
42
42
  before { exporter.instance_variable_set(:@locale, 'fr') }
43
43
 
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Aigu::Importer do
3
+ describe Aigu::RailsImporter do
4
4
  describe :build_blob do
5
- let(:importer) { Aigu::Importer.new }
5
+ let(:importer) { Aigu::RailsImporter.new }
6
6
  let(:expanded_content) { importer.send(:expand_content_values, content) }
7
7
 
8
8
  let(:content) do
@@ -37,7 +37,7 @@ describe Aigu::Importer do
37
37
  end
38
38
 
39
39
  describe :localize_content_keys do
40
- let(:exporter) { Aigu::Importer.new }
40
+ let(:exporter) { Aigu::RailsImporter.new }
41
41
  let(:localized_keys) { exporter.send(:localize_content_keys, content).keys }
42
42
  before { exporter.instance_variable_set(:@locale, 'fr') }
43
43
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aigu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémi Prévost
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-09-22 00:00:00.000000000 Z
12
+ date: 2015-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -108,6 +108,7 @@ files:
108
108
  - ".gitignore"
109
109
  - ".rspec"
110
110
  - ".rubocop.yml"
111
+ - ".travis.yml"
111
112
  - Gemfile
112
113
  - LICENSE.md
113
114
  - README.md
@@ -122,24 +123,28 @@ files:
122
123
  - lib/aigu/cli.rb
123
124
  - lib/aigu/core_exporter.rb
124
125
  - lib/aigu/core_importer.rb
126
+ - lib/aigu/ember_exporter.rb
127
+ - lib/aigu/ember_importer.rb
125
128
  - lib/aigu/exporter.rb
126
129
  - lib/aigu/extensions/hash.rb
127
130
  - lib/aigu/importer.rb
128
131
  - lib/aigu/ios_exporter.rb
129
132
  - lib/aigu/ios_importer.rb
130
- - lib/aigu/mergeer.rb
133
+ - lib/aigu/merger.rb
131
134
  - lib/aigu/puller.rb
132
135
  - lib/aigu/pusher.rb
133
- - lib/aigu/unmergeer.rb
136
+ - lib/aigu/rails_exporter.rb
137
+ - lib/aigu/rails_importer.rb
138
+ - lib/aigu/unmerger.rb
134
139
  - lib/aigu/version.rb
135
140
  - spec/aigu/android_exporter_spec.rb
136
141
  - spec/aigu/android_importer_spec.rb
137
142
  - spec/aigu/core_exporter_spec.rb
138
143
  - spec/aigu/core_importer_spec.rb
139
- - spec/aigu/exporter_spec.rb
140
- - spec/aigu/importer_spec.rb
141
144
  - spec/aigu/ios_exporter_spec.rb
142
145
  - spec/aigu/ios_importer_spec.rb
146
+ - spec/aigu/rails_exporter_spec.rb
147
+ - spec/aigu/rails_importer_spec.rb
143
148
  - spec/spec_helper.rb
144
149
  homepage: https://github.com/mirego/aigu
145
150
  licenses:
@@ -171,8 +176,8 @@ test_files:
171
176
  - spec/aigu/android_importer_spec.rb
172
177
  - spec/aigu/core_exporter_spec.rb
173
178
  - spec/aigu/core_importer_spec.rb
174
- - spec/aigu/exporter_spec.rb
175
- - spec/aigu/importer_spec.rb
176
179
  - spec/aigu/ios_exporter_spec.rb
177
180
  - spec/aigu/ios_importer_spec.rb
181
+ - spec/aigu/rails_exporter_spec.rb
182
+ - spec/aigu/rails_importer_spec.rb
178
183
  - spec/spec_helper.rb