localio 0.1.7 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +28 -0
  3. data/.gitignore +4 -1
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile.lock +134 -0
  7. data/README.md +36 -34
  8. data/bin/localize +10 -7
  9. data/docs/plans/2026-02-23-modernization-design.md +91 -0
  10. data/docs/plans/2026-02-23-modernization.md +1699 -0
  11. data/docs/plans/2026-02-23-twine-writer-design.md +72 -0
  12. data/docs/plans/2026-02-23-twine-writer.md +267 -0
  13. data/lib/localio/localizable_writer.rb +4 -1
  14. data/lib/localio/processors/csv_processor.rb +1 -1
  15. data/lib/localio/processors/google_drive_processor.rb +19 -45
  16. data/lib/localio/processors/xlsx_processor.rb +1 -1
  17. data/lib/localio/template_handler.rb +3 -1
  18. data/lib/localio/templates/android_localizable.erb +14 -2
  19. data/lib/localio/templates/ios_constant_localizable.erb +16 -2
  20. data/lib/localio/templates/ios_localizable.erb +20 -5
  21. data/lib/localio/templates/java_properties_localizable.erb +16 -2
  22. data/lib/localio/templates/json_localizable.erb +6 -5
  23. data/lib/localio/templates/rails_localizable.erb +15 -3
  24. data/lib/localio/templates/resx_localizable.erb +14 -2
  25. data/lib/localio/templates/swift_constant_localizable.erb +15 -2
  26. data/lib/localio/version.rb +1 -1
  27. data/lib/localio/writers/ios_writer.rb +3 -3
  28. data/lib/localio/writers/swift_writer.rb +3 -3
  29. data/lib/localio/writers/twine_writer.rb +48 -0
  30. data/localio.gemspec +19 -25
  31. data/spec/fixtures/sample.csv +11 -0
  32. data/spec/localio/filter_spec.rb +40 -0
  33. data/spec/localio/formatter_spec.rb +32 -0
  34. data/spec/localio/processors/csv_processor_spec.rb +89 -0
  35. data/spec/localio/processors/google_drive_processor_spec.rb +107 -0
  36. data/spec/localio/processors/xls_processor_spec.rb +65 -0
  37. data/spec/localio/processors/xlsx_processor_spec.rb +59 -0
  38. data/spec/localio/segment_spec.rb +27 -0
  39. data/spec/localio/segments_list_holder_spec.rb +22 -0
  40. data/spec/localio/string_helper_spec.rb +49 -0
  41. data/spec/localio/template_handler_spec.rb +67 -0
  42. data/spec/localio/term_spec.rb +24 -0
  43. data/spec/localio/writers/android_writer_spec.rb +71 -0
  44. data/spec/localio/writers/ios_writer_spec.rb +63 -0
  45. data/spec/localio/writers/java_properties_writer_spec.rb +35 -0
  46. data/spec/localio/writers/json_writer_spec.rb +57 -0
  47. data/spec/localio/writers/rails_writer_spec.rb +47 -0
  48. data/spec/localio/writers/resx_writer_spec.rb +44 -0
  49. data/spec/localio/writers/swift_writer_spec.rb +42 -0
  50. data/spec/localio/writers/twine_writer_spec.rb +68 -0
  51. data/spec/localio_spec.rb +62 -0
  52. data/spec/spec_helper.rb +24 -0
  53. data/spec/support/shared_terms.rb +35 -0
  54. metadata +61 -46
@@ -0,0 +1,72 @@
1
+ # Twine Writer Design
2
+
3
+ **Date:** 2026-02-23
4
+ **Status:** Approved
5
+
6
+ ## Goal
7
+
8
+ Add a `:twine` platform writer that generates a [Twine](https://github.com/scelis/twine)-compatible `strings.txt` file containing all languages in a single file.
9
+
10
+ ## Output Format
11
+
12
+ Standard Twine format with tab indentation. All languages are written per key, not per file.
13
+
14
+ ```
15
+ [[section_name]]
16
+ [key_name]
17
+ en = English value
18
+ es = Spanish value
19
+ comment = Optional comment
20
+
21
+ [another_key]
22
+ en = Another value
23
+ es = Otro valor
24
+ ```
25
+
26
+ ## Term Mapping
27
+
28
+ | Localio term | Twine output |
29
+ |---|---|
30
+ | `[init-node]` | `[[section_name]]` (value from default language) |
31
+ | `[end-node]` | blank line (sections close implicitly) |
32
+ | `[comment]` | buffered; written as `comment = ...` under the next real key |
33
+ | regular key | `[key]` block with one `lang = value` line per language |
34
+
35
+ ## Architecture
36
+
37
+ **Approach:** Pure Ruby writer (no ERB template). The comment-buffering logic and single-file-all-languages structure don't fit ERB well.
38
+
39
+ ### Writer class
40
+
41
+ - **File:** `lib/localio/writers/twine_writer.rb`
42
+ - **Class:** `TwineWriter`
43
+ - **Interface:** `self.write(languages, terms, path, formatter, options)` — matches all existing writers
44
+
45
+ ### Algorithm (single pass)
46
+
47
+ 1. Open output file (`strings.txt` or `options[:output_file]`)
48
+ 2. `pending_comment = nil`
49
+ 3. For each term:
50
+ - `[comment]` → store `pending_comment` from default language value
51
+ - `[init-node]` → write `[[value]]`, reset `pending_comment`
52
+ - `[end-node]` → write blank line
53
+ - regular key → write `[key]` block with all lang translations; if `pending_comment` is set, append `\t\tcomment = ...` and clear it
54
+
55
+ ### Platform registration
56
+
57
+ - Registered in `lib/localio.rb` as `:twine`
58
+ - Locfile usage: `platform :twine` or `platform :twine, :output_file => 'custom.txt'`
59
+
60
+ ### Key formatting
61
+
62
+ - Smart formatter defaults to snake_case (consistent with android/rails)
63
+
64
+ ## Testing
65
+
66
+ `spec/localio/writers/twine_writer_spec.rb` using the existing `standard terms` shared context and `Dir.mktmpdir` isolation. Cases:
67
+
68
+ - Creates `strings.txt` in the output path
69
+ - All languages present in each key block
70
+ - `[init-node]` produces `[[section]]` header
71
+ - `[comment]` row attaches as `comment = ...` to the following key
72
+ - `:output_file` option overrides the default filename
@@ -0,0 +1,267 @@
1
+ # Twine Writer Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Add a `:twine` platform writer that generates a single Twine-compatible `strings.txt` containing all languages.
6
+
7
+ **Architecture:** Pure Ruby writer (no ERB template). Single-pass over terms: buffers `[comment]` rows and attaches them to the next real key, maps `[init-node]`/`[end-node]` to Twine `[[section]]` headers, writes all language translations per key. Registered in `localizable_writer.rb` alongside the existing 7 writers.
8
+
9
+ **Tech Stack:** Ruby 3.2+, RSpec 3.x, stdlib FileUtils
10
+
11
+ ---
12
+
13
+ ## Task 1: Write the TwineWriter spec (failing)
14
+
15
+ **Files:**
16
+ - Create: `spec/localio/writers/twine_writer_spec.rb`
17
+
18
+ **Step 1: Create the spec file**
19
+
20
+ ```ruby
21
+ require 'localio/string_helper'
22
+ require 'localio/term'
23
+ require 'localio/formatter'
24
+ require 'localio/writers/twine_writer'
25
+
26
+ RSpec.describe TwineWriter do
27
+ include_context 'standard terms'
28
+ # standard terms provides: languages {'en'=>1,'es'=>2,'fr'=>3},
29
+ # default_language 'en', and terms:
30
+ # [comment] "Section General", app_name, greeting, dots_test, ampersand_test
31
+
32
+ let(:options) { { default_language: 'en' } }
33
+
34
+ describe '.write' do
35
+ it 'creates strings.txt in the output path' do
36
+ Dir.mktmpdir do |tmpdir|
37
+ Dir.chdir(tmpdir) { TwineWriter.write(languages, terms, tmpdir, :smart, options) }
38
+ expect(File).to exist(File.join(tmpdir, 'strings.txt'))
39
+ end
40
+ end
41
+
42
+ it 'uses a custom filename when :output_file is specified' do
43
+ Dir.mktmpdir do |tmpdir|
44
+ Dir.chdir(tmpdir) do
45
+ TwineWriter.write(languages, terms, tmpdir, :smart, options.merge(output_file: 'translations.txt'))
46
+ end
47
+ expect(File).to exist(File.join(tmpdir, 'translations.txt'))
48
+ expect(File).not_to exist(File.join(tmpdir, 'strings.txt'))
49
+ end
50
+ end
51
+
52
+ it 'includes all languages for each key' do
53
+ Dir.mktmpdir do |tmpdir|
54
+ Dir.chdir(tmpdir) { TwineWriter.write(languages, terms, tmpdir, :smart, options) }
55
+ content = File.read(File.join(tmpdir, 'strings.txt'))
56
+ expect(content).to include('en = My App')
57
+ expect(content).to include('es = Mi Aplicación')
58
+ expect(content).to include('fr = Mon Application')
59
+ end
60
+ end
61
+
62
+ it 'writes [init-node] terms as [[section]] headers' do
63
+ section_terms = [
64
+ Term.new('[init-node]').tap { |t| t.values['en'] = 'General'; t.values['es'] = 'General'; t.values['fr'] = 'General' },
65
+ Term.new('app_name').tap { |t| t.values['en'] = 'My App'; t.values['es'] = 'Mi App'; t.values['fr'] = 'Mon App' },
66
+ Term.new('[end-node]').tap { |t| t.values['en'] = 'end'; t.values['es'] = 'end'; t.values['fr'] = 'end' },
67
+ ]
68
+ Dir.mktmpdir do |tmpdir|
69
+ Dir.chdir(tmpdir) { TwineWriter.write(languages, section_terms, tmpdir, :smart, options) }
70
+ content = File.read(File.join(tmpdir, 'strings.txt'))
71
+ expect(content).to include('[[General]]')
72
+ end
73
+ end
74
+
75
+ it 'attaches [comment] value as comment = on the following key' do
76
+ Dir.mktmpdir do |tmpdir|
77
+ Dir.chdir(tmpdir) { TwineWriter.write(languages, terms, tmpdir, :smart, options) }
78
+ content = File.read(File.join(tmpdir, 'strings.txt'))
79
+ # [comment] "Section General" appears before app_name and attaches to it
80
+ expect(content).to include("\t\tcomment = Section General")
81
+ # The comment block appears before the greeting key block
82
+ comment_pos = content.index('comment = Section General')
83
+ greeting_pos = content.index('[greeting]')
84
+ expect(comment_pos).to be < greeting_pos
85
+ end
86
+ end
87
+ end
88
+ end
89
+ ```
90
+
91
+ **Step 2: Run the spec to confirm it fails with "uninitialized constant TwineWriter"**
92
+
93
+ ```bash
94
+ bundle exec rspec spec/localio/writers/twine_writer_spec.rb --format documentation 2>&1
95
+ ```
96
+
97
+ Expected: `LoadError` or `NameError: uninitialized constant TwineWriter`
98
+
99
+ ---
100
+
101
+ ## Task 2: Implement TwineWriter
102
+
103
+ **Files:**
104
+ - Create: `lib/localio/writers/twine_writer.rb`
105
+
106
+ **Step 1: Create the writer**
107
+
108
+ ```ruby
109
+ require 'fileutils'
110
+ require 'localio/formatter'
111
+
112
+ class TwineWriter
113
+ def self.write(languages, terms, path, formatter, options)
114
+ puts 'Writing Twine translations...'
115
+
116
+ default_language = options[:default_language]
117
+ output_filename = options[:output_file] || 'strings.txt'
118
+
119
+ FileUtils.mkdir_p(path)
120
+
121
+ File.open(File.join(path, output_filename), 'w') do |f|
122
+ pending_comment = nil
123
+
124
+ terms.each do |term|
125
+ if term.is_comment?
126
+ pending_comment = term.values[default_language]
127
+ elsif term.keyword == '[init-node]'
128
+ f.puts "[[#{term.values[default_language]}]]"
129
+ pending_comment = nil
130
+ elsif term.keyword == '[end-node]'
131
+ f.puts ''
132
+ pending_comment = nil
133
+ else
134
+ key = Formatter.format(term.keyword, formatter, method(:twine_key_formatter))
135
+ f.puts "\t[#{key}]"
136
+ languages.keys.each do |lang|
137
+ f.puts "\t\t#{lang} = #{term.values[lang]}"
138
+ end
139
+ if pending_comment
140
+ f.puts "\t\tcomment = #{pending_comment}"
141
+ pending_comment = nil
142
+ end
143
+ f.puts ''
144
+ end
145
+ end
146
+ end
147
+
148
+ puts " > #{output_filename.yellow}"
149
+ end
150
+
151
+ private
152
+
153
+ def self.twine_key_formatter(key)
154
+ key.space_to_underscore.strip_tag.downcase
155
+ end
156
+ end
157
+ ```
158
+
159
+ **Step 2: Run the spec to confirm all 5 tests pass**
160
+
161
+ ```bash
162
+ bundle exec rspec spec/localio/writers/twine_writer_spec.rb --format documentation 2>&1
163
+ ```
164
+
165
+ Expected: `5 examples, 0 failures`
166
+
167
+ **Step 3: Run the full suite to confirm no regressions**
168
+
169
+ ```bash
170
+ bundle exec rspec --format progress 2>&1 | tail -5
171
+ ```
172
+
173
+ Expected: `112 examples, 0 failures`
174
+
175
+ **Step 4: Commit**
176
+
177
+ ```bash
178
+ git add lib/localio/writers/twine_writer.rb spec/localio/writers/twine_writer_spec.rb
179
+ git commit -m "feat: add TwineWriter for Twine-compatible strings.txt output"
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Task 3: Register :twine in LocalizableWriter
185
+
186
+ **Files:**
187
+ - Modify: `lib/localio/localizable_writer.rb`
188
+
189
+ **Step 1: Add the require and case branch**
190
+
191
+ At the top of `lib/localio/localizable_writer.rb`, add after the last `require` line:
192
+
193
+ ```ruby
194
+ require 'localio/writers/twine_writer'
195
+ ```
196
+
197
+ In the `case platform` block, add before the `else`:
198
+
199
+ ```ruby
200
+ when :twine
201
+ TwineWriter.write languages, terms, path, formatter, options
202
+ ```
203
+
204
+ Also update the error message in the `else` branch to include `:twine`:
205
+
206
+ ```ruby
207
+ raise ArgumentError, 'Platform not supported! Current possibilities are :android, :ios, :json, :rails, :java_properties, :resx, :twine'
208
+ ```
209
+
210
+ **Step 2: Run the full suite to confirm nothing broke**
211
+
212
+ ```bash
213
+ bundle exec rspec --format progress 2>&1 | tail -5
214
+ ```
215
+
216
+ Expected: `112 examples, 0 failures`
217
+
218
+ **Step 3: Commit**
219
+
220
+ ```bash
221
+ git add lib/localio/localizable_writer.rb
222
+ git commit -m "feat: register :twine platform in LocalizableWriter"
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Task 4: Update README
228
+
229
+ **Files:**
230
+ - Modify: `README.md`
231
+
232
+ **Step 1: Add :twine to the supported platforms list**
233
+
234
+ Find the `#### Supported platforms` section and add after the `:resx` bullet:
235
+
236
+ ```markdown
237
+ * `:twine` for [Twine](https://github.com/scelis/twine)-compatible `strings.txt` files containing all languages in a single file. The `output_path` is the directory where the file will be written.
238
+ ```
239
+
240
+ **Step 2: Add a Twine source section after the ResX platform parameters section**
241
+
242
+ Find the `#### Supported sources` section header and add a new platform parameters sub-section before it (after the ResX section):
243
+
244
+ ```markdown
245
+ ##### Twine - :twine
246
+
247
+ By default the output file is named `strings.txt`. Use `:output_file` to override:
248
+
249
+ ````ruby
250
+ platform :twine, :output_file => 'MyApp.strings'
251
+ ````
252
+ ```
253
+
254
+ **Step 3: Run the full suite one final time**
255
+
256
+ ```bash
257
+ bundle exec rspec --format progress 2>&1 | tail -5
258
+ ```
259
+
260
+ Expected: `112 examples, 0 failures`
261
+
262
+ **Step 4: Commit**
263
+
264
+ ```bash
265
+ git add README.md
266
+ git commit -m "docs: document :twine platform in README"
267
+ ```
@@ -5,6 +5,7 @@ require 'localio/writers/json_writer'
5
5
  require 'localio/writers/rails_writer'
6
6
  require 'localio/writers/java_properties_writer'
7
7
  require 'localio/writers/resx_writer'
8
+ require 'localio/writers/twine_writer'
8
9
 
9
10
  module LocalizableWriter
10
11
  def self.write(platform, languages, terms, path, formatter, options)
@@ -23,8 +24,10 @@ module LocalizableWriter
23
24
  JavaPropertiesWriter.write languages, terms, path, formatter, options
24
25
  when :resx
25
26
  ResXWriter.write languages, terms, path, formatter, options
27
+ when :twine
28
+ TwineWriter.write languages, terms, path, formatter, options
26
29
  else
27
- raise ArgumentError, 'Platform not supported! Current possibilities are :android, :ios, :json, :rails, :java_properties, :resx'
30
+ raise ArgumentError, 'Platform not supported! Current possibilities are :android, :ios, :json, :rails, :java_properties, :resx, :twine'
28
31
  end
29
32
  end
30
33
  end
@@ -42,7 +42,7 @@ class CsvProcessor
42
42
  unless platform_options[:avoid_lang_downcase]
43
43
  default_language = default_language.downcase
44
44
  lang = lang.downcase
45
- end
45
+ end
46
46
 
47
47
  unless col_text.to_s == ''
48
48
  languages.store lang, column
@@ -1,6 +1,5 @@
1
1
  require 'google_drive'
2
2
  require 'localio/term'
3
- require 'localio/config_store'
4
3
 
5
4
  class GoogleDriveProcessor
6
5
 
@@ -19,9 +18,11 @@ class GoogleDriveProcessor
19
18
  client_id = options[:client_id]
20
19
  client_secret = options[:client_secret]
21
20
 
22
- # We need client_id / client_secret
23
- raise ArgumentError, ':client_id required for Google Drive. Check how to get it here: https://developers.google.com/drive/web/auth/web-server' if client_id.nil?
24
- raise ArgumentError, ':client_secret required for Google Drive. Check how to get it here: https://developers.google.com/drive/web/auth/web-server' if client_secret.nil?
21
+ # We need client_id / client_secret (unless a service-account key is supplied)
22
+ unless options[:client_token].is_a?(String) && File.file?(options[:client_token].to_s)
23
+ raise ArgumentError, ':client_id required for Google Drive. Check how to get it here: https://developers.google.com/drive/web/auth/web-server' if client_id.nil?
24
+ raise ArgumentError, ':client_secret required for Google Drive. Check how to get it here: https://developers.google.com/drive/web/auth/web-server' if client_secret.nil?
25
+ end
25
26
 
26
27
  override_default = nil
27
28
  override_default = platform_options[:override_default] unless platform_options.nil? or platform_options[:override_default].nil?
@@ -29,50 +30,23 @@ class GoogleDriveProcessor
29
30
  # Log in and get spreadsheet
30
31
  puts 'Logging in to Google Drive...'
31
32
  begin
32
- client = Google::APIClient.new application_name: 'Localio', application_version: Localio::VERSION
33
- auth = client.authorization
34
- auth.client_id = client_id
35
- auth.client_secret = client_secret
36
- auth.scope =
37
- "https://docs.google.com/feeds/" +
38
- "https://www.googleapis.com/auth/drive " +
39
- "https://spreadsheets.google.com/feeds/"
40
- auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
41
-
42
- config = ConfigStore.new
43
-
44
- access_token = nil
45
-
46
- if options.has_key?(:client_token)
47
- puts 'Refreshing auth token...'
48
- auth.refresh_token = options[:client_token]
49
- auth.refresh!
50
- access_token = auth.access_token
51
- elsif config.has? :refresh_token
52
- puts 'Refreshing auth token...'
53
- auth.refresh_token = config.get :refresh_token
54
- auth.refresh!
55
- access_token = auth.access_token
33
+ session = nil
34
+
35
+ # Service-account key file (JSON) path supplied via :client_token
36
+ if options[:client_token].is_a?(String) && File.file?(options[:client_token].to_s)
37
+ session = GoogleDrive::Session.from_service_account_key(options[:client_token])
56
38
  else
57
- puts "1. Open this page in your browser:\n#{auth.authorization_uri}\n\n"
58
- puts "2. Enter the authorization code shown in the page: "
59
- auth.code = $stdin.gets.chomp
60
- auth.fetch_access_token!
61
- access_token = auth.access_token
39
+ # OAuth2 config-file flow (from_config saves/loads the refresh token)
40
+ config_path = File.join(Dir.home, '.localio_gdrive_config.json')
41
+ session = GoogleDrive::Session.from_config(
42
+ config_path,
43
+ client_id: client_id,
44
+ client_secret: client_secret
45
+ )
62
46
  end
63
-
64
- if !options.has_key?(:client_token)
65
- puts 'Store auth data...'
66
- config.store :refresh_token, auth.refresh_token
67
- config.store :access_token, auth.access_token
68
- config.persist
69
- end
70
-
71
- # Creates a session
72
- session = GoogleDrive.login_with_oauth(access_token)
73
47
  rescue => e
74
48
  puts "Error: #{e.inspect}"
75
- raise 'Couldn\'t access Google Drive. Check your values for :client_id and :client_secret, and delete :access_token if present (you might need to refresh its value so please remove it)'
49
+ raise 'Couldn\'t access Google Drive. Check your values for :client_id and :client_secret.'
76
50
  end
77
51
  puts 'Logged in!'
78
52
 
@@ -125,7 +99,7 @@ class GoogleDriveProcessor
125
99
  unless platform_options[:avoid_lang_downcase]
126
100
  default_language = default_language.downcase
127
101
  lang = lang.downcase
128
- end
102
+ end
129
103
 
130
104
  unless col_text.to_s == ''
131
105
  languages.store lang, column
@@ -49,7 +49,7 @@ class XlsxProcessor
49
49
  unless platform_options[:avoid_lang_downcase]
50
50
  default_language = default_language.downcase
51
51
  lang = lang.downcase
52
- end
52
+ end
53
53
 
54
54
  unless col_text.to_s == ''
55
55
  languages.store lang, column
@@ -6,11 +6,13 @@ class TemplateHandler
6
6
  full_template_path = File.join(File.dirname(File.expand_path(__FILE__)), "templates/#{template_name}")
7
7
  input_file = File.open(full_template_path, 'rb')
8
8
  template = input_file.read
9
+
9
10
  input_file.close
10
11
  renderer = ERB.new(template)
11
12
  output = renderer.result(segments.get_binding)
12
13
  output_file = File.new(generated_file_name, 'w')
13
- output_file.write(output)
14
+ output_replace = output.gsub(",}", "}")
15
+ output_file.write(output_replace)
14
16
  output_file.close
15
17
 
16
18
  destination_path = File.join(target_directory, generated_file_name)
@@ -1,11 +1,23 @@
1
1
  <!-- Localizable created with localio. DO NOT MODIFY. -->
2
2
  <resources>
3
3
  <%
4
+ node_keys =[]
4
5
  @segments.each do |term|
5
6
  if term.is_comment? %>
6
7
  <!-- <%= term.translation %> -->
7
- <% else %> <string name="<%= term.key %>"><%= term.translation %></string>
8
- <% end
8
+ <% else
9
+ if term.key == '[init-node]' or term.key == '[end-node]'
10
+ node_keys << term.translation if term.key == '[init-node]'
11
+ node_keys.pop if term.key == '[end-node]'
12
+ else
13
+ if node_keys.length() > 0
14
+ key_join = node_keys.join("_")+"_"+term.key
15
+ else
16
+ key_join = term.key
17
+ end
18
+ %> <string name="<%= key_join %>"><%= term.translation %></string>
19
+ <% end
20
+ end
9
21
  end
10
22
  %>
11
23
  </resources>
@@ -6,6 +6,20 @@ GENERATED - DO NOT MODIFY - use localio instead.
6
6
  Created by localio.
7
7
  */
8
8
 
9
- <% @segments.each do |term| %>
10
- #define <%= term.key %> NSLocalizedString(@"<%= term.translation %>",nil)<%
9
+ <%
10
+ node_keys = []
11
+ @segments.each do |term|
12
+ if term.key == '[init-node]' or term.key == '[end-node]'
13
+ node_keys << term.translation if term.key == '[init-node]'
14
+ node_keys.pop if term.key == '[end-node]'
15
+ else
16
+ if node_keys.length() >0
17
+ key_join = node_keys.join("_").capitalize+"_"+term.key.downcase
18
+ else
19
+ key_join = term.key
20
+ end
21
+ %>
22
+ #define kLocale<%= key_join %> NSLocalizedString(@"_<%= key_join %>",nil)
23
+ <%
24
+ end
11
25
  end %>
@@ -6,12 +6,27 @@ GENERATED - DO NOT MODIFY - use the localio gem instead.
6
6
  Created by localio.
7
7
  */
8
8
 
9
- <% @segments.each do |term| %>
10
- <%
11
- if term.is_comment?
9
+ <%
10
+ node_keys = []
11
+ @segments.each do |term|
12
+ if term.is_comment?
13
+ %>
14
+ // <%= term.translation %>
15
+ <%
16
+ else
17
+ if term.key == '[init-node]' or term.key == '[end-node]'
18
+ node_keys << term.translation if term.key == '[init-node]'
19
+ node_keys.pop if term.key == '[end-node]'
20
+ else
21
+ if node_keys.length() > 0
22
+ key_join = node_keys.join("_").capitalize+"_"+term.key.downcase
23
+ else
24
+ key_join = term.key
25
+ end
12
26
  %>
13
- // <%= term.translation %>
14
- <% else %>"<%= term.key %>" = "<%= term.translation %>";<%
27
+ "_<%= key_join %>" = "<%= term.translation %>";
28
+ <%
29
+ end
15
30
  end
16
31
  end
17
32
  %>
@@ -1,10 +1,24 @@
1
1
  # Localizable created with localio. DO NOT MODIFY.
2
2
  #
3
3
  # Language: <%= @language %>
4
- <% @segments.each do |term|
4
+ <%
5
+ node_keys = []
6
+ @segments.each do |term|
5
7
  if term.is_comment? %>
6
8
  # <%= term.translation %>
7
- <% else %><%= term.key %>=<%= term.translation %>
9
+ <%
10
+ else
11
+ if term.key == '[init-node]' or term.key == '[end-node]'
12
+ node_keys << term.translation if term.key == '[init-node]'
13
+ node_keys.pop if term.key == '[end-node]'
14
+ else
15
+ if node_keys.length() > 0
16
+ key_join = node_keys.join("_")+"_"+term.key
17
+ else
18
+ key_join = term.key
19
+ end
20
+ %><%= key_join %>=<%= term.translation %>
8
21
  <% end
9
22
  end
23
+ end
10
24
  %>
@@ -4,12 +4,13 @@
4
4
  "language": "<%= @language %>"
5
5
  },
6
6
  "translations": {
7
- <% @segments.each do |term|
7
+ <% @segments.each.with_index do |term, index|
8
8
  term_value = term.translation
9
9
  term_key = term.key
10
-
11
- term_key = '___comment___' if term.is_comment?
12
- %> "<%= term_key %>": "<%= term_value %>"<% unless term == @segments.last %>,<% end %>
13
- <% end %>
10
+ count_to_string = index.to_s
11
+ term_key = '___comment_'+count_to_string+'___' if term.is_comment?
12
+ if term.key == '[init-node]'%>
13
+ "<%= term_value %>": {<% elsif term.key == '[end-node]'%>}<% unless term == @segments.last %>,<% end %><% else %>
14
+ "<%= term_key %>": "<%= term_value %>"<% unless term == @segments.last %>,<% end %><% end end %>
14
15
  }
15
16
  }
@@ -4,10 +4,22 @@
4
4
 
5
5
  <%= @language %>:
6
6
  <%
7
+ node_keys = []
7
8
  @segments.each do |term|
8
- if term.is_comment? %>
9
+ if term.is_comment? %>
9
10
  # <%= term.translation %>
10
- <% else %> <%= term.key %>: "<%= term.translation %>"
11
- <% end
11
+ <% else
12
+ if term.key == '[init-node]' or term.key == '[end-node]'
13
+ node_keys << term.translation if term.key == '[init-node]'
14
+ node_keys.pop if term.key == '[end-node]'
15
+ else
16
+ if node_keys.length() > 0
17
+ key_join = node_keys.join("_")+"_"+term.key
18
+ else
19
+ key_join = term.key
20
+ end
21
+ %> <%= key_join %>: "<%= term.translation %>"
22
+ <% end
12
23
  end
24
+ end
13
25
  %>
@@ -122,14 +122,26 @@
122
122
  <comment>Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language.</comment>
123
123
  </data>
124
124
  <%
125
+ node_keys = []
125
126
  @segments.each do |term|
126
127
  if term.is_comment? %>
127
128
  <!-- <%= term.translation %> -->
128
- <% else %> <data name="<%= term.key %>" xml:space="preserve">
129
+ <% else
130
+ if term.key == '[init-node]' or term.key == '[end-node]'
131
+ node_keys << term.translation if term.key == '[init-node]'
132
+ node_keys.pop if term.key == '[end-node]'
133
+ else
134
+ if node_keys.length() > 0
135
+ key_join = node_keys.join().capitalize+term.key.capitalize
136
+ else
137
+ key_join = term.key
138
+ end
139
+ %> <data name="<%= key_join %>" xml:space="preserve">
129
140
  <value><![CDATA[<%= term.translation %>]]></value>
130
141
  <comment/>
131
142
  </data>
132
- <% end
143
+ <% end
144
+ end
133
145
  end
134
146
  %>
135
147