localio 0.1.6 → 0.2.0

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 (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +28 -0
  3. data/.gitignore +3 -1
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile.lock +138 -0
  7. data/README.md +34 -35
  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/lib/localio/processor.rb +2 -1
  12. data/lib/localio/processors/csv_processor.rb +12 -3
  13. data/lib/localio/processors/google_drive_processor.rb +36 -48
  14. data/lib/localio/processors/xls_processor.rb +12 -3
  15. data/lib/localio/processors/xlsx_processor.rb +12 -3
  16. data/lib/localio/template_handler.rb +3 -1
  17. data/lib/localio/templates/android_localizable.erb +14 -2
  18. data/lib/localio/templates/ios_constant_localizable.erb +16 -2
  19. data/lib/localio/templates/ios_localizable.erb +20 -5
  20. data/lib/localio/templates/java_properties_localizable.erb +16 -2
  21. data/lib/localio/templates/json_localizable.erb +6 -5
  22. data/lib/localio/templates/rails_localizable.erb +15 -3
  23. data/lib/localio/templates/resx_localizable.erb +14 -2
  24. data/lib/localio/templates/swift_constant_localizable.erb +15 -2
  25. data/lib/localio/version.rb +1 -1
  26. data/lib/localio/writers/ios_writer.rb +3 -3
  27. data/lib/localio/writers/swift_writer.rb +3 -3
  28. data/localio.gemspec +19 -25
  29. data/spec/fixtures/sample.csv +11 -0
  30. data/spec/localio/filter_spec.rb +40 -0
  31. data/spec/localio/formatter_spec.rb +32 -0
  32. data/spec/localio/processors/csv_processor_spec.rb +89 -0
  33. data/spec/localio/processors/google_drive_processor_spec.rb +107 -0
  34. data/spec/localio/processors/xls_processor_spec.rb +65 -0
  35. data/spec/localio/processors/xlsx_processor_spec.rb +59 -0
  36. data/spec/localio/segment_spec.rb +27 -0
  37. data/spec/localio/segments_list_holder_spec.rb +22 -0
  38. data/spec/localio/string_helper_spec.rb +49 -0
  39. data/spec/localio/template_handler_spec.rb +67 -0
  40. data/spec/localio/term_spec.rb +24 -0
  41. data/spec/localio/writers/android_writer_spec.rb +71 -0
  42. data/spec/localio/writers/ios_writer_spec.rb +63 -0
  43. data/spec/localio/writers/java_properties_writer_spec.rb +35 -0
  44. data/spec/localio/writers/json_writer_spec.rb +57 -0
  45. data/spec/localio/writers/rails_writer_spec.rb +47 -0
  46. data/spec/localio/writers/resx_writer_spec.rb +44 -0
  47. data/spec/localio/writers/swift_writer_spec.rb +42 -0
  48. data/spec/localio_spec.rb +62 -0
  49. data/spec/spec_helper.rb +24 -0
  50. data/spec/support/shared_terms.rb +35 -0
  51. metadata +60 -49
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7545745288cd1d2a90ab07b92aabeba59481e21b
4
- data.tar.gz: fa6c825a442d45737232b8095c6d952040456c3f
2
+ SHA256:
3
+ metadata.gz: ab5c7db7901943df5125445d1151851ba8831d9407dca76a43f08f7127499639
4
+ data.tar.gz: a2ad9aab66aea7dcea8f41b0c7653d7cde6b8e78b3bdb0390fe1e8ae474f67c2
5
5
  SHA512:
6
- metadata.gz: 36f73ead59451a89bd13b31a677fb5ff92ce410f1379f8c315abb0be6aaa1d8754b98573f27a02aba0915bfb578f580eaa95e60574789a27e36778604b247d3a
7
- data.tar.gz: bb797d0d11972c9b269443b03bfee43d683e60ffbe78abb68218f2b8c816d73c7028962cc8cbdf9d243ab3affda172c6468cbd881c5af5f004faae4ce4a51e2a
6
+ metadata.gz: 04d467b401399b5a7810bdbd2fa1d95cb820c0333942ee36a68bd5ec298596229649d4a665c2c35deae8e9fff5ebead2aa941433960b3fb83410be6d9297a4cd
7
+ data.tar.gz: 7e3a0348a4d4cc70f8aca67fd52e5bba3b0986df6622e7c000beb8fe6690a28c8bec9c500935868eda2d468263fdcedf1e5996b458cc37ddfc88528bea9d5cc7
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ test:
11
+ name: Ruby ${{ matrix.ruby }}
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ ruby: ["3.2", "3.3"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true
26
+
27
+ - name: Run tests
28
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -17,4 +17,6 @@ test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
19
  .idea/
20
- testing/
20
+ testing/
21
+ .vscode/launch.json
22
+ .worktrees/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --require spec_helper
2
+ --format documentation
3
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.10
data/Gemfile.lock ADDED
@@ -0,0 +1,138 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ localio (0.1.8)
5
+ csv (>= 3.2)
6
+ google_drive (~> 3.0)
7
+ nokogiri (~> 1.16)
8
+ simple_xlsx_reader (~> 2.0)
9
+ spreadsheet (~> 1.3)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.8.8)
15
+ public_suffix (>= 2.0.2, < 8.0)
16
+ base64 (0.3.0)
17
+ bigdecimal (4.0.1)
18
+ csv (3.3.5)
19
+ declarative (0.0.20)
20
+ diff-lcs (1.6.2)
21
+ faraday (1.10.5)
22
+ faraday-em_http (~> 1.0)
23
+ faraday-em_synchrony (~> 1.0)
24
+ faraday-excon (~> 1.1)
25
+ faraday-httpclient (~> 1.0)
26
+ faraday-multipart (~> 1.0)
27
+ faraday-net_http (~> 1.0)
28
+ faraday-net_http_persistent (~> 1.0)
29
+ faraday-patron (~> 1.0)
30
+ faraday-rack (~> 1.0)
31
+ faraday-retry (~> 1.0)
32
+ ruby2_keywords (>= 0.0.4)
33
+ faraday-em_http (1.0.0)
34
+ faraday-em_synchrony (1.0.1)
35
+ faraday-excon (1.1.0)
36
+ faraday-httpclient (1.0.1)
37
+ faraday-multipart (1.2.0)
38
+ multipart-post (~> 2.0)
39
+ faraday-net_http (1.0.2)
40
+ faraday-net_http_persistent (1.2.0)
41
+ faraday-patron (1.0.0)
42
+ faraday-rack (1.0.0)
43
+ faraday-retry (1.0.3)
44
+ google-apis-core (0.11.3)
45
+ addressable (~> 2.5, >= 2.5.1)
46
+ googleauth (>= 0.16.2, < 2.a)
47
+ httpclient (>= 2.8.1, < 3.a)
48
+ mini_mime (~> 1.0)
49
+ representable (~> 3.0)
50
+ retriable (>= 2.0, < 4.a)
51
+ rexml
52
+ google-apis-drive_v3 (0.46.0)
53
+ google-apis-core (>= 0.11.0, < 2.a)
54
+ google-apis-sheets_v4 (0.26.0)
55
+ google-apis-core (>= 0.11.0, < 2.a)
56
+ google_drive (3.0.7)
57
+ google-apis-drive_v3 (>= 0.5.0, < 1.0.0)
58
+ google-apis-sheets_v4 (>= 0.4.0, < 1.0.0)
59
+ googleauth (>= 0.5.0, < 1.0.0)
60
+ nokogiri (>= 1.5.3, < 2.0.0)
61
+ googleauth (0.17.1)
62
+ faraday (>= 0.17.3, < 2.0)
63
+ jwt (>= 1.4, < 3.0)
64
+ memoist (~> 0.16)
65
+ multi_json (~> 1.11)
66
+ os (>= 0.9, < 2.0)
67
+ signet (~> 0.15)
68
+ httpclient (2.9.0)
69
+ mutex_m
70
+ jwt (2.10.2)
71
+ base64
72
+ logger (1.7.0)
73
+ memoist (0.16.2)
74
+ mini_mime (1.1.5)
75
+ mini_portile2 (2.8.9)
76
+ multi_json (1.19.1)
77
+ multipart-post (2.4.1)
78
+ mutex_m (0.3.0)
79
+ nokogiri (1.19.1)
80
+ mini_portile2 (~> 2.8.2)
81
+ racc (~> 1.4)
82
+ nokogiri (1.19.1-arm64-darwin)
83
+ racc (~> 1.4)
84
+ nokogiri (1.19.1-x86_64-linux-gnu)
85
+ racc (~> 1.4)
86
+ os (1.1.4)
87
+ public_suffix (7.0.2)
88
+ racc (1.8.1)
89
+ rake (13.3.1)
90
+ representable (3.2.0)
91
+ declarative (< 0.1.0)
92
+ trailblazer-option (>= 0.1.1, < 0.2.0)
93
+ uber (< 0.2.0)
94
+ retriable (3.2.1)
95
+ rexml (3.4.4)
96
+ rspec (3.13.2)
97
+ rspec-core (~> 3.13.0)
98
+ rspec-expectations (~> 3.13.0)
99
+ rspec-mocks (~> 3.13.0)
100
+ rspec-core (3.13.6)
101
+ rspec-support (~> 3.13.0)
102
+ rspec-expectations (3.13.5)
103
+ diff-lcs (>= 1.2.0, < 2.0)
104
+ rspec-support (~> 3.13.0)
105
+ rspec-mocks (3.13.7)
106
+ diff-lcs (>= 1.2.0, < 2.0)
107
+ rspec-support (~> 3.13.0)
108
+ rspec-support (3.13.7)
109
+ ruby-ole (1.2.13.1)
110
+ ruby2_keywords (0.0.5)
111
+ rubyzip (3.2.2)
112
+ signet (0.21.0)
113
+ addressable (~> 2.8)
114
+ faraday (>= 0.17.5, < 3.a)
115
+ jwt (>= 1.5, < 4.0)
116
+ multi_json (~> 1.10)
117
+ simple_xlsx_reader (2.0.1)
118
+ nokogiri
119
+ rubyzip
120
+ spreadsheet (1.3.4)
121
+ bigdecimal
122
+ logger
123
+ ruby-ole
124
+ trailblazer-option (0.1.2)
125
+ uber (0.1.0)
126
+
127
+ PLATFORMS
128
+ arm64-darwin
129
+ ruby
130
+ x86_64-linux
131
+
132
+ DEPENDENCIES
133
+ localio!
134
+ rake
135
+ rspec (~> 3.0)
136
+
137
+ BUNDLED WITH
138
+ 2.5.22
data/README.md CHANGED
@@ -51,7 +51,7 @@ source :xlsx,
51
51
  :path => 'my_translations.xlsx'
52
52
  ````
53
53
 
54
- This would connect localio to your Google Drive and process the spreadsheet with title "[Localizables] My Project!".
54
+ This would read from `my_translations.xlsx` and write iOS localizable files to `my_output_path/`.
55
55
 
56
56
  The list of possible commands is this.
57
57
 
@@ -76,6 +76,11 @@ Option | Description
76
76
 
77
77
  #### Extra platform parameters
78
78
 
79
+
80
+ ##### `avoid_lang_downcase`
81
+
82
+ By default, language codes are downcased. We can set `:avoid_lang_downcase => true` to avoid this behavior.
83
+
79
84
  ##### iOS - :ios, :swift
80
85
 
81
86
  We can opt-out from the constants/macros. We will simple need to add `:create_constants => false`. By default, if omitted, the constants will be always created. It's a good practice to have a compile-time check of the existence of your keys; but if you don't like it it's fine.
@@ -104,57 +109,51 @@ platform :resx, :resource_file => "WebResources"
104
109
 
105
110
  `source :google_drive` will get the translation strings from Google Drive.
106
111
 
107
- You will have to provide some required parameters too. Here is a list of all the parameters.
112
+ Two authentication methods are supported: **OAuth2** (for personal accounts) and **service accounts** (for automated/CI use).
108
113
 
109
114
  Option | Description
110
115
  ----------------------------|-------------------------------------------------------------------------
111
116
  `:spreadsheet` | (Req.) Title of the spreadsheet you want to use. Can be a partial match.
112
- `:login` | **DEPRECATED** This is deprecated starting version 0.1.0. Please remove it.
113
- `:password` | **DEPRECATED** This is deprecated starting version 0.1.0. Please remove it.
114
- `:client_id` | (Req.) Your Google CLIENT ID.
115
- `:client_secret` | (Req.) Your Google CLIENT SECRET.
116
-
117
- Please take into account that from version 0.1.0 of Localio onwards we are using **Google OAuth2 authentication**, as the previous one with login/password has been deprecated by Google and cannot be access anymore starting April 20th 2015.
117
+ `:sheet` | (Req.) Index number (starting with 0) or name of the sheet w/ the data
118
+ `:client_id` | Your Google OAuth2 Client ID. Required unless using `:client_token`.
119
+ `:client_secret` | Your Google OAuth2 Client Secret. Required unless using `:client_token`.
120
+ `:client_token` | Path to a service account JSON key file. Alternative to OAuth2.
118
121
 
119
- Setting it up is a bit of a pain, although it is only required the first time and can be shared by all your projects:
122
+ ###### Option A: OAuth2 (personal account)
120
123
 
121
- 1. You have to create a new project in Google Developers Console for using Drive API. You can do that [here](https://console.developers.google.com/flows/enableapi?apiid=drive).
122
- 2. After it is created you will be redirected to the credentials section (if not, just select under APIs and authentication in the sidebar the Credentials section), where you will click in the button labeled **Create new client ID**.
123
- 3. Select the third option, the one that says something like **Installed Application**.
124
- 4. Fill the form with whatever you want. For example, you could put Localio as the product name (the only thing required there).
125
- 5. Select again the third option, **Installed Application**, and in the platform selector select the last one, **Others**.
126
- 6. You will have all the necessary information in the next screen: Client ID and Client Secret.
124
+ 1. Go to the [Google Cloud Console](https://console.cloud.google.com/), create a project and enable the **Google Drive API**.
125
+ 2. Under **APIs & Services Credentials**, create an **OAuth client ID**. Choose **Desktop app** as the application type.
126
+ 3. Download or copy your **Client ID** and **Client Secret**.
127
127
 
128
- After doing all this, you are ready to add `:client_id` and `:client_secret` fields to your Locfile `source`. It will look somewhat like this at this stage:
128
+ Add them to your Locfile:
129
129
 
130
130
  ```ruby
131
131
  source :google_drive,
132
132
  :spreadsheet => '[Localizables] My Project',
133
- :client_id => 'XXXXXXXXX-XXXXXXXX.apps.googleusercontent.com',
134
- :client_secret => 'asdFFGGhjKlzxcvbnm'
133
+ :client_id => ENV['GOOGLE_CLIENT_ID'],
134
+ :client_secret => ENV['GOOGLE_CLIENT_SECRET']
135
135
  ```
136
136
 
137
- Then, the first time you run it, you will be prompted to follow some instructions. You will be asked to open a website, where you will be prompted for permission to use the Drive API. After you allow it, you will be given an authorization code, which you will have to paste in your terminal screen when prompted.
137
+ The first time you run `localize`, you will be prompted to open a URL in your browser, grant access to your Drive, and paste the authorization code back into the terminal. After that, the refresh token is saved to `~/.localio_gdrive_config.json` and subsequent runs authenticate automatically.
138
138
 
139
- **NOTE** A hidden file, called .localio.yml, will be created in your Locfile directory. You should **add that file to your ignored resources** in your repository, aka the **.gitignore** file.
139
+ **NOTE** As it is a very bad practice to put your sensitive information in a plain file, it is **strongly recommended** to use environment variables. Export them from your shell profile (`.zshrc`, `.bashrc`, etc.):
140
140
 
141
- **NOTE** As it is a very bad practice to put your sensitive information in a plain file, specially when you would want to upload your project to some repository, it is **VERY RECOMMENDED** that you use environment variables in here. Ruby syntax is accepted so you can use `ENV['CLIENT_SECRET']` and `ENV['CLIENT_ID']` in here.
142
-
143
- For example, this.
141
+ ```bash
142
+ export GOOGLE_CLIENT_ID="your_client_id"
143
+ export GOOGLE_CLIENT_SECRET="your_client_secret"
144
+ ```
144
145
 
145
- ````ruby
146
- source :google_drive,
147
- :spreadsheet => '[Localizables] My Project!',
148
- :client_id => ENV['CLIENT_ID'],
149
- :client_secret => ENV['CLIENT_SECRET']
150
- ````
146
+ ###### Option B: Service account (automated/CI use)
151
147
 
152
- And in your .bashrc (or .bash_profile, .zshrc or whatever), you could export those environment variables like this:
148
+ 1. In the [Google Cloud Console](https://console.cloud.google.com/), create a **Service Account** under **APIs & Services Credentials**.
149
+ 2. Download the JSON key file.
150
+ 3. Share the target spreadsheet with the service account's email address (found in the JSON file under `client_email`).
153
151
 
154
- ````ruby
155
- export CLIENT_ID="your_client_id"
156
- export CLIENT_SECRET="your_client_secret"
157
- ````
152
+ ```ruby
153
+ source :google_drive,
154
+ :spreadsheet => '[Localizables] My Project',
155
+ :client_token => 'path/to/service_account_key.json'
156
+ ```
158
157
 
159
158
  ##### XLS
160
159
 
@@ -180,7 +179,7 @@ You may specify a `sheet` parameter, otherwise the first sheet will be used.
180
179
  Option | Description
181
180
  ----------------------------|-------------------------------------------------------------------------
182
181
  `:path` | (Req.) Path for your XLSX file.
183
- `:sheet` | (Optional) Index number (starting with 0) or name of the sheet w/ the data
182
+ `:sheet` | (Req.) Index number (starting with 0) or name of the sheet w/ the data
184
183
 
185
184
  ````ruby
186
185
  source :xlsx,
data/bin/localize CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: UTF-8
3
-
2
+ require 'optparse'
4
3
  require 'localio'
5
4
 
6
- begin
7
- Localio.from_cmdline(ARGV)
8
- rescue => e
9
- abort e.message
10
- end
5
+ OptionParser.new do |opts|
6
+ opts.banner = 'Usage: localize [Locfile]'
7
+ opts.on('-v', '--version', 'Show version') do
8
+ require 'localio/version'
9
+ puts Localio::VERSION
10
+ exit
11
+ end
12
+ end.parse!
11
13
 
14
+ Localio.from_cmdline(ARGV)
@@ -0,0 +1,91 @@
1
+ # Localio Modernization Design
2
+
3
+ **Date:** 2026-02-23
4
+ **Approach:** Option A — tests first, then dependency updates
5
+
6
+ ## Overview
7
+
8
+ Modernize the Localio gem in two phases:
9
+
10
+ 1. Write a comprehensive RSpec test suite with fixtures and mocks
11
+ 2. Update all dependencies to current versions and target Ruby 3.x, using the test suite as a safety net
12
+
13
+ ## Phase 1: Test Suite
14
+
15
+ ### Structure
16
+
17
+ ```
18
+ spec/
19
+ spec_helper.rb
20
+ fixtures/
21
+ sample.csv # canonical test data: keys in 3 languages, special chars, comments, multi-level keys
22
+ sample.xlsx # small binary fixture
23
+ sample.xls # small binary fixture
24
+ localio/
25
+ term_spec.rb
26
+ segment_spec.rb
27
+ filter_spec.rb
28
+ formatter_spec.rb
29
+ template_handler_spec.rb
30
+ processors/
31
+ csv_processor_spec.rb
32
+ xlsx_processor_spec.rb
33
+ xls_processor_spec.rb
34
+ google_drive_processor_spec.rb # mocked worksheet interface
35
+ writers/
36
+ android_writer_spec.rb
37
+ ios_writer_spec.rb
38
+ swift_writer_spec.rb
39
+ json_writer_spec.rb
40
+ rails_writer_spec.rb
41
+ java_properties_writer_spec.rb
42
+ resx_writer_spec.rb
43
+ localio_spec.rb # end-to-end: CSV fixture → writer → verify output files
44
+ ```
45
+
46
+ ### Fixture Data
47
+
48
+ A single canonical `sample.csv` covers all test scenarios:
49
+ - Normal keys with translations in 3 languages
50
+ - Special characters: ampersands, ellipsis, printf format strings
51
+ - Comment rows
52
+ - Multi-level/nested keys (dot-separated) for JSON nesting tests
53
+
54
+ The same fixture data drives all processor and writer tests for consistency.
55
+
56
+ ### Testing Strategy Per Layer
57
+
58
+ **Models (Term, Segment):** Construction, attribute access, `is_comment?` detection.
59
+
60
+ **Filter:** `only` and `except` with regex patterns against a fixed segment list.
61
+
62
+ **Formatter:** All 4 modes (`:smart`, `:none`, `:camel_case`, `:snake_case`) against varied key strings.
63
+
64
+ **Processors:**
65
+ - CSV/XLSX/XLS: Parse real fixture files, assert correct Term extraction, language detection, comment handling
66
+ - Google Drive: Mock the gem's worksheet interface, test the same parsing logic in isolation
67
+
68
+ **TemplateHandler:** Render ERB templates, write to `Dir.mktmpdir`, assert output matches expected content.
69
+
70
+ **Writers (all 7):** Feed a fixed Terms array → call writer → assert output files in a temp dir contain expected strings (spot-check key lines, not byte-perfect comparison).
71
+
72
+ **Pipeline (`localio_spec.rb`):** CSV fixture → Android + JSON writers → verify files exist with correct content.
73
+
74
+ ### Key Test Helpers
75
+ - Shared `let(:terms)` factory via RSpec shared contexts
76
+ - `Dir.mktmpdir` for output isolation in all writer and template tests
77
+ - No network calls; Google Drive mocked at the worksheet interface level
78
+
79
+ ## Phase 2: Dependency Updates
80
+
81
+ | Gem | Current | Target | Notes |
82
+ |-----|---------|--------|-------|
83
+ | `google_drive` | `~> 1.0` | `~> 3.0` | API changed; processor needs update |
84
+ | `spreadsheet` | `~> 1.0` | `~> 1.3` | Minor updates only |
85
+ | `simple_xlsx_reader` | `~> 1.0` | `~> 2.0` | Breaking changes in v2 |
86
+ | `nokogiri` | `~> 1.6` | `~> 1.16` | Mostly drop-in |
87
+ | `micro-optparse` | `~> 1.2` | remove → stdlib `optparse` | Unmaintained |
88
+ | `bundler` | `~> 1.3` | `~> 2.0` | Dev dep |
89
+ | Ruby | `>= 1.9.2` | `>= 3.0` | Gemspec update |
90
+
91
+ The green test suite from Phase 1 is the safety net — failures after dep updates pinpoint exactly what broke.