remote_translation_loader 1.0.4 → 2.0.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.
- checksums.yaml +4 -4
- data/README.md +62 -18
- data/lib/remote_translation_loader/configuration.rb +16 -0
- data/lib/remote_translation_loader/errors.rb +15 -0
- data/lib/remote_translation_loader/fetchers/base_fetcher.rb +18 -4
- data/lib/remote_translation_loader/fetchers/file_fetcher.rb +4 -7
- data/lib/remote_translation_loader/fetchers/http_fetcher.rb +31 -4
- data/lib/remote_translation_loader/fetchers/s3_fetcher.rb +25 -7
- data/lib/remote_translation_loader/loader.rb +25 -8
- data/lib/remote_translation_loader/railtie.rb +22 -0
- data/lib/remote_translation_loader/source_resolver.rb +41 -0
- data/lib/remote_translation_loader/version.rb +1 -1
- data/lib/remote_translation_loader.rb +30 -1
- data/mise.toml +2 -0
- metadata +17 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59ac9fed7d52b1170939188c6351ff9f1cadb3fce0d75a4449abd88cbb072d83
|
|
4
|
+
data.tar.gz: 36876a8b0aa0420dd664edad81d3996c633fc15d9d74837705975818d40826c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85f5a569008fd4af358e37eb52863dc13f1e9d985623843d9f696488856bb460d98e54db331d42c0c4686fae3dba586caa9e72924db622b69df468c48cf5e016
|
|
7
|
+
data.tar.gz: 4b4060fc821ee2eb958c59e96beb0d0b5a961859c3257f14679bd122eafd979dbeea7ac118e6f3c5b8bc9efed5b300bf70a26b36df37b3ff40648f23d9633260
|
data/README.md
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
1
|
# RemoteTranslationLoader
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/remote_translation_loader)
|
|
4
|
+
[](https://badge.fury.io/rb/remote_translation_loader)
|
|
5
|
+
[](https://github.com/gklsan/remote_translation_loader/network)
|
|
6
|
+
[](https://github.com/gklsan/remote_translation_loader/stargazers)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
**RemoteTranslationLoader** is a Ruby gem designed to dynamically fetch and load translation files (YAML or JSON) into your Ruby or Ruby on Rails application. It supports multiple sources such as HTTP URLs, local files, and AWS S3, allowing you to seamlessly integrate external translations.
|
|
4
11
|
|
|
5
12
|
---
|
|
6
13
|
|
|
7
14
|
## **Features**
|
|
8
15
|
|
|
9
|
-
- Fetch translations from multiple sources:
|
|
10
|
-
- **HTTP URLs**
|
|
16
|
+
- Fetch translations from multiple sources, auto-detected from the source string:
|
|
17
|
+
- **HTTP(S) URLs**
|
|
11
18
|
- **Local files**
|
|
12
|
-
- **AWS S3 buckets**
|
|
19
|
+
- **AWS S3 buckets** (`s3://bucket/key`)
|
|
20
|
+
- Mix sources of different kinds in a single call — no need to instantiate a fetcher per type.
|
|
21
|
+
- Supports both **YAML** and **JSON** translation files.
|
|
13
22
|
- Supports deep merging of translations with existing `I18n` backend.
|
|
14
23
|
- Namespace support for isolating translations.
|
|
15
24
|
- Dry-run mode to simulate translation loading.
|
|
16
|
-
-
|
|
25
|
+
- Automatic retry with backoff for transient HTTP failures.
|
|
26
|
+
- Structured error classes (`FetchError`, `ParseError`, `ValidationError`) instead of generic runtime errors.
|
|
27
|
+
- Rake task and Rails `Railtie` for zero-config integration.
|
|
17
28
|
- CLI tool for manual loading.
|
|
18
29
|
|
|
19
30
|
---
|
|
@@ -42,21 +53,42 @@ gem install remote_translation_loader
|
|
|
42
53
|
|
|
43
54
|
## **Usage**
|
|
44
55
|
|
|
45
|
-
### **Basic Usage**
|
|
56
|
+
### **Basic Usage — auto-detected sources**
|
|
57
|
+
|
|
58
|
+
The simplest way to use the gem: just pass a list of sources. Each one is
|
|
59
|
+
auto-detected as HTTP, a local file, or S3 (`s3://bucket/key`) — you can freely
|
|
60
|
+
mix all three in one call:
|
|
46
61
|
|
|
47
|
-
#### **1. HTTP Fetching**
|
|
48
62
|
```ruby
|
|
49
63
|
require 'remote_translation_loader'
|
|
50
64
|
|
|
65
|
+
RemoteTranslationLoader.load([
|
|
66
|
+
'https://example.com/en.yml',
|
|
67
|
+
'/path/to/local/fr.yml',
|
|
68
|
+
's3://my-bucket/translations/de.yml'
|
|
69
|
+
])
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`RemoteTranslationLoader.load(sources, **options)` is sugar for
|
|
73
|
+
`RemoteTranslationLoader::Loader.new(sources).fetch_and_load(**options)`.
|
|
74
|
+
|
|
75
|
+
Note: using an `s3://` source requires the `aws-sdk-s3` gem — add
|
|
76
|
+
`gem 'aws-sdk-s3'` to your Gemfile if you fetch from S3.
|
|
77
|
+
|
|
78
|
+
### **Explicit fetchers**
|
|
79
|
+
|
|
80
|
+
You can still force every source through a specific fetcher instead of
|
|
81
|
+
auto-detection:
|
|
82
|
+
|
|
83
|
+
#### **1. HTTP Fetching**
|
|
84
|
+
```ruby
|
|
51
85
|
urls = ['https://example.com/en.yml', 'https://example.com/fr.yml']
|
|
52
|
-
loader = RemoteTranslationLoader::Loader.new(urls)
|
|
86
|
+
loader = RemoteTranslationLoader::Loader.new(urls, fetcher: RemoteTranslationLoader::Fetchers::HttpFetcher.new)
|
|
53
87
|
loader.fetch_and_load
|
|
54
88
|
```
|
|
55
89
|
|
|
56
90
|
#### **2. Local File Fetching**
|
|
57
91
|
```ruby
|
|
58
|
-
require 'remote_translation_loader'
|
|
59
|
-
|
|
60
92
|
files = ['/path/to/local/en.yml', '/path/to/local/fr.yml']
|
|
61
93
|
loader = RemoteTranslationLoader::Loader.new(files, fetcher: RemoteTranslationLoader::Fetchers::FileFetcher.new)
|
|
62
94
|
loader.fetch_and_load
|
|
@@ -64,10 +96,8 @@ loader.fetch_and_load
|
|
|
64
96
|
|
|
65
97
|
#### **3. AWS S3 Fetching**
|
|
66
98
|
```ruby
|
|
67
|
-
require 'remote_translation_loader'
|
|
68
|
-
|
|
69
99
|
bucket = 'your-s3-bucket'
|
|
70
|
-
s3_fetcher = RemoteTranslationLoader::Fetchers::S3Fetcher.new(bucket, region: 'us-east-1')
|
|
100
|
+
s3_fetcher = RemoteTranslationLoader::Fetchers::S3Fetcher.new(bucket, s3_client: Aws::S3::Client.new(region: 'us-east-1'))
|
|
71
101
|
keys = ['translations/en.yml', 'translations/fr.yml']
|
|
72
102
|
|
|
73
103
|
loader = RemoteTranslationLoader::Loader.new(keys, fetcher: s3_fetcher)
|
|
@@ -109,7 +139,22 @@ remote_translation_loader https://example.com/en.yml /path/to/local/fr.yml
|
|
|
109
139
|
|
|
110
140
|
## **Rails Integration**
|
|
111
141
|
|
|
112
|
-
### **1.
|
|
142
|
+
### **1. Zero-config with the Railtie (recommended)**
|
|
143
|
+
|
|
144
|
+
When `remote_translation_loader` is loaded inside a Rails app, its `Railtie` is
|
|
145
|
+
required automatically. Configure sources once and translations load on boot,
|
|
146
|
+
and refresh on every reload in development:
|
|
147
|
+
|
|
148
|
+
#### `config/initializers/remote_translation_loader.rb`
|
|
149
|
+
```ruby
|
|
150
|
+
RemoteTranslationLoader.configure do |config|
|
|
151
|
+
config.sources = ['https://example.com/en.yml', 's3://my-bucket/fr.yml']
|
|
152
|
+
config.namespace = 'remote'
|
|
153
|
+
config.logger = Rails.logger
|
|
154
|
+
end
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### **3. Rake Task**
|
|
113
158
|
Use the provided Rake task to fetch translations in a Rails application:
|
|
114
159
|
|
|
115
160
|
#### Add this to your `Rakefile`:
|
|
@@ -123,17 +168,16 @@ load 'remote_translation_loader/tasks/remote_translation_loader.rake'
|
|
|
123
168
|
rake translations:load[https://example.com/en.yml,/path/to/local/fr.yml]
|
|
124
169
|
```
|
|
125
170
|
|
|
126
|
-
### **
|
|
171
|
+
### **4. Manual Initializer**
|
|
127
172
|
|
|
128
|
-
|
|
173
|
+
If you'd rather not use the Railtie, load translations explicitly:
|
|
129
174
|
|
|
130
175
|
#### `config/initializers/remote_translation_loader.rb`
|
|
131
176
|
```ruby
|
|
132
177
|
require 'remote_translation_loader'
|
|
133
178
|
|
|
134
179
|
urls = ['https://example.com/en.yml', '/path/to/local/fr.yml']
|
|
135
|
-
|
|
136
|
-
loader.fetch_and_load(namespace: 'remote')
|
|
180
|
+
RemoteTranslationLoader.load(urls, namespace: 'remote')
|
|
137
181
|
```
|
|
138
182
|
|
|
139
183
|
---
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "logger"
|
|
4
|
+
|
|
5
|
+
module RemoteTranslationLoader
|
|
6
|
+
class Configuration
|
|
7
|
+
attr_accessor :sources, :namespace, :dry_run, :logger
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@sources = []
|
|
11
|
+
@namespace = nil
|
|
12
|
+
@dry_run = false
|
|
13
|
+
@logger = Logger.new($stdout)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RemoteTranslationLoader
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
# Raised when a fetcher can't retrieve content from its source
|
|
7
|
+
# (network failure, missing file, S3 error, etc.).
|
|
8
|
+
class FetchError < Error; end
|
|
9
|
+
|
|
10
|
+
# Raised when fetched content can't be parsed as YAML/JSON.
|
|
11
|
+
class ParseError < Error; end
|
|
12
|
+
|
|
13
|
+
# Raised when parsed content isn't a valid translations hash.
|
|
14
|
+
class ValidationError < Error; end
|
|
15
|
+
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
3
6
|
module RemoteTranslationLoader
|
|
4
7
|
module Fetchers
|
|
5
8
|
class BaseFetcher
|
|
@@ -7,10 +10,21 @@ module RemoteTranslationLoader
|
|
|
7
10
|
raise NotImplementedError, "Subclasses must implement the `fetch` method"
|
|
8
11
|
end
|
|
9
12
|
|
|
10
|
-
def parse(content)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
def parse(content, format: :yaml)
|
|
14
|
+
case format
|
|
15
|
+
when :json
|
|
16
|
+
JSON.parse(content)
|
|
17
|
+
else
|
|
18
|
+
YAML.safe_load(content, aliases: true)
|
|
19
|
+
end
|
|
20
|
+
rescue Psych::SyntaxError, Psych::DisallowedClass, JSON::ParserError => e
|
|
21
|
+
raise ParseError, "Failed to parse content: #{e.message}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Infers :json vs :yaml from a source's file extension. Defaults to :yaml
|
|
25
|
+
# (covers .yml/.yaml and extension-less sources like plain URLs).
|
|
26
|
+
def format_for(source)
|
|
27
|
+
File.extname(source.to_s).delete_prefix(".").downcase == "json" ? :json : :yaml
|
|
14
28
|
end
|
|
15
29
|
end
|
|
16
30
|
end
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'http'
|
|
4
|
-
|
|
5
3
|
module RemoteTranslationLoader
|
|
6
4
|
module Fetchers
|
|
7
|
-
class
|
|
8
|
-
def fetch(
|
|
9
|
-
|
|
10
|
-
raise "Failed to fetch data from #{url}" unless response.status.success?
|
|
5
|
+
class FileFetcher < BaseFetcher
|
|
6
|
+
def fetch(path)
|
|
7
|
+
raise FetchError, "File not found: #{path}" unless File.exist?(path)
|
|
11
8
|
|
|
12
|
-
parse(
|
|
9
|
+
parse(File.read(path), format: format_for(path))
|
|
13
10
|
end
|
|
14
11
|
end
|
|
15
12
|
end
|
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "http"
|
|
4
|
+
|
|
3
5
|
module RemoteTranslationLoader
|
|
4
6
|
module Fetchers
|
|
5
|
-
class
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
class HttpFetcher < BaseFetcher
|
|
8
|
+
DEFAULT_RETRIES = 3
|
|
9
|
+
DEFAULT_TIMEOUT = 10 # seconds
|
|
10
|
+
RETRYABLE_ERRORS = [HTTP::Error, HTTP::TimeoutError, HTTP::ConnectionError].freeze
|
|
11
|
+
|
|
12
|
+
def initialize(retries: DEFAULT_RETRIES, timeout: DEFAULT_TIMEOUT)
|
|
13
|
+
@retries = retries
|
|
14
|
+
@timeout = timeout
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fetch(url)
|
|
18
|
+
response = with_retries(url) { HTTP.timeout(@timeout).get(url) }
|
|
19
|
+
raise FetchError, "Failed to fetch data from #{url} (status #{response.status})" unless response.status.success?
|
|
20
|
+
|
|
21
|
+
parse(response.body.to_s, format: format_for(url))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def with_retries(url)
|
|
27
|
+
attempt = 0
|
|
28
|
+
begin
|
|
29
|
+
attempt += 1
|
|
30
|
+
yield
|
|
31
|
+
rescue *RETRYABLE_ERRORS => e
|
|
32
|
+
raise FetchError, "Failed to fetch data from #{url}: #{e.message}" if attempt > @retries
|
|
8
33
|
|
|
9
|
-
|
|
34
|
+
sleep(2**(attempt - 1) * 0.1)
|
|
35
|
+
retry
|
|
36
|
+
end
|
|
10
37
|
end
|
|
11
38
|
end
|
|
12
39
|
end
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'aws-sdk-s3'
|
|
4
|
-
|
|
5
3
|
module RemoteTranslationLoader
|
|
6
4
|
module Fetchers
|
|
7
5
|
class S3Fetcher < BaseFetcher
|
|
8
|
-
def initialize(s3_client
|
|
9
|
-
@
|
|
6
|
+
def initialize(bucket, s3_client: nil)
|
|
7
|
+
@bucket = bucket
|
|
8
|
+
@s3_client_override = s3_client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def fetch(key)
|
|
12
|
+
response = s3_client.get_object(bucket: @bucket, key: key)
|
|
13
|
+
parse(response.body.read, format: format_for(key))
|
|
14
|
+
rescue Aws::S3::Errors::ServiceError => e
|
|
15
|
+
raise FetchError, "Failed to fetch #{key} from S3 bucket #{@bucket}: #{e.message}"
|
|
10
16
|
end
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# Building the real Aws::S3::Client (and requiring aws-sdk-s3) is deferred
|
|
21
|
+
# until the first actual fetch, so simply constructing an S3Fetcher never
|
|
22
|
+
# requires the aws-sdk-s3 gem or AWS credentials/region to be configured.
|
|
23
|
+
def s3_client
|
|
24
|
+
@s3_client ||= @s3_client_override || begin
|
|
25
|
+
begin
|
|
26
|
+
require "aws-sdk-s3"
|
|
27
|
+
rescue LoadError
|
|
28
|
+
raise LoadError, "S3Fetcher requires the aws-sdk-s3 gem. Add `gem 'aws-sdk-s3'` to your Gemfile."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Aws::S3::Client.new
|
|
32
|
+
end
|
|
15
33
|
end
|
|
16
34
|
end
|
|
17
35
|
end
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "i18n"
|
|
2
|
+
require "logger"
|
|
2
3
|
|
|
3
4
|
module RemoteTranslationLoader
|
|
4
5
|
class Loader
|
|
5
|
-
|
|
6
|
+
# `fetcher:` can be left unset to auto-detect the right fetcher per source
|
|
7
|
+
# (http(s):// -> HttpFetcher, s3://bucket/key -> S3Fetcher, else -> FileFetcher).
|
|
8
|
+
# Pass an explicit fetcher to force every source through it instead.
|
|
9
|
+
def initialize(urls, fetcher: nil, logger: Logger.new($stdout))
|
|
6
10
|
@urls = urls
|
|
7
11
|
@fetcher = fetcher
|
|
12
|
+
@resolver = SourceResolver.new if @fetcher.nil?
|
|
13
|
+
@logger = logger
|
|
8
14
|
end
|
|
9
15
|
|
|
10
16
|
def fetch_and_load(dry_run: false, namespace: nil)
|
|
11
17
|
@urls.each do |url|
|
|
12
|
-
content =
|
|
13
|
-
validate_translations!(content)
|
|
18
|
+
content = fetch_content(url)
|
|
19
|
+
validate_translations!(content, url)
|
|
14
20
|
|
|
15
21
|
if dry_run
|
|
16
|
-
|
|
22
|
+
@logger.info("Simulating loading for #{url}: #{content.inspect}")
|
|
17
23
|
else
|
|
18
24
|
merge_into_i18n(content, namespace: namespace)
|
|
19
25
|
end
|
|
@@ -22,8 +28,19 @@ module RemoteTranslationLoader
|
|
|
22
28
|
|
|
23
29
|
private
|
|
24
30
|
|
|
25
|
-
def
|
|
26
|
-
|
|
31
|
+
def fetch_content(url)
|
|
32
|
+
if @fetcher
|
|
33
|
+
@fetcher.fetch(url)
|
|
34
|
+
else
|
|
35
|
+
fetcher, key = @resolver.resolve(url)
|
|
36
|
+
fetcher.fetch(key)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate_translations!(translations, source)
|
|
41
|
+
return if translations.is_a?(Hash)
|
|
42
|
+
|
|
43
|
+
raise ValidationError, "Invalid translations format for #{source}: expected a Hash, got #{translations.class}"
|
|
27
44
|
end
|
|
28
45
|
|
|
29
46
|
def merge_into_i18n(content, namespace: nil)
|
|
@@ -35,7 +52,7 @@ module RemoteTranslationLoader
|
|
|
35
52
|
merged = deep_merge(existing, translations)
|
|
36
53
|
I18n.backend.store_translations(locale.to_sym, merged)
|
|
37
54
|
end
|
|
38
|
-
|
|
55
|
+
@logger.info("Translations loaded successfully!")
|
|
39
56
|
end
|
|
40
57
|
|
|
41
58
|
def deep_merge(existing, incoming)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RemoteTranslationLoader
|
|
4
|
+
# Zero-config Rails integration. Configure once:
|
|
5
|
+
#
|
|
6
|
+
# RemoteTranslationLoader.configure do |config|
|
|
7
|
+
# config.sources = ["https://example.com/en.yml", "s3://bucket/fr.yml"]
|
|
8
|
+
# config.namespace = "remote"
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# and translations are fetched on boot, and refreshed on every reload in
|
|
12
|
+
# development.
|
|
13
|
+
class Railtie < ::Rails::Railtie
|
|
14
|
+
initializer "remote_translation_loader.load_translations" do
|
|
15
|
+
RemoteTranslationLoader.load_configured_translations
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
config.to_prepare do
|
|
19
|
+
RemoteTranslationLoader.load_configured_translations if Rails.env.development?
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RemoteTranslationLoader
|
|
4
|
+
# Given a plain source string, resolves which Fetcher should handle it and
|
|
5
|
+
# what key/path/url to pass to that fetcher's `#fetch`. This lets a single
|
|
6
|
+
# Loader mix HTTP, local file, and S3 sources in one call without the
|
|
7
|
+
# caller having to construct fetchers manually.
|
|
8
|
+
#
|
|
9
|
+
# s3://my-bucket/path/to/en.yml -> S3Fetcher
|
|
10
|
+
# http(s)://... -> HttpFetcher
|
|
11
|
+
# anything else -> FileFetcher
|
|
12
|
+
class SourceResolver
|
|
13
|
+
S3_URI = %r{\As3://(?<bucket>[^/]+)/(?<key>.+)\z}.freeze
|
|
14
|
+
HTTP_URI = %r{\Ahttps?://}i.freeze
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@s3_fetchers = {}
|
|
18
|
+
@http_fetcher = Fetchers::HttpFetcher.new
|
|
19
|
+
@file_fetcher = Fetchers::FileFetcher.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns [fetcher, key] where `fetcher.fetch(key)` retrieves the content.
|
|
23
|
+
def resolve(source)
|
|
24
|
+
str = source.to_s
|
|
25
|
+
|
|
26
|
+
if (match = S3_URI.match(str))
|
|
27
|
+
[s3_fetcher_for(match[:bucket]), match[:key]]
|
|
28
|
+
elsif HTTP_URI.match?(str)
|
|
29
|
+
[@http_fetcher, str]
|
|
30
|
+
else
|
|
31
|
+
[@file_fetcher, str]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def s3_fetcher_for(bucket)
|
|
38
|
+
@s3_fetchers[bucket] ||= Fetchers::S3Fetcher.new(bucket)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -1,11 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "remote_translation_loader/version"
|
|
4
|
-
require_relative "remote_translation_loader/
|
|
4
|
+
require_relative "remote_translation_loader/errors"
|
|
5
|
+
require_relative "remote_translation_loader/configuration"
|
|
5
6
|
require_relative "remote_translation_loader/fetchers/base_fetcher"
|
|
6
7
|
require_relative "remote_translation_loader/fetchers/http_fetcher"
|
|
7
8
|
require_relative "remote_translation_loader/fetchers/file_fetcher"
|
|
8
9
|
require_relative "remote_translation_loader/fetchers/s3_fetcher"
|
|
10
|
+
require_relative "remote_translation_loader/source_resolver"
|
|
11
|
+
require_relative "remote_translation_loader/loader"
|
|
9
12
|
|
|
10
13
|
module RemoteTranslationLoader
|
|
14
|
+
class << self
|
|
15
|
+
# One-liner convenience: RemoteTranslationLoader.load(["en.yml", "https://.../fr.yml"])
|
|
16
|
+
def load(sources, **options)
|
|
17
|
+
Loader.new(sources).fetch_and_load(**options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def configuration
|
|
21
|
+
@configuration ||= Configuration.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configure
|
|
25
|
+
yield(configuration) if block_given?
|
|
26
|
+
configuration
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Used by the Railtie to fetch+load whatever was set via `.configure`.
|
|
30
|
+
def load_configured_translations
|
|
31
|
+
config = configuration
|
|
32
|
+
return if config.sources.empty?
|
|
33
|
+
|
|
34
|
+
Loader.new(config.sources, logger: config.logger)
|
|
35
|
+
.fetch_and_load(dry_run: config.dry_run, namespace: config.namespace)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
11
38
|
end
|
|
39
|
+
|
|
40
|
+
require_relative "remote_translation_loader/railtie" if defined?(Rails::Railtie)
|
data/mise.toml
ADDED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: remote_translation_loader
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gokul (gklsan)
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 2026-07-07 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: http
|
|
@@ -25,19 +24,19 @@ dependencies:
|
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: '5.0'
|
|
27
26
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
27
|
+
name: i18n
|
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
|
30
29
|
requirements:
|
|
31
|
-
- -
|
|
30
|
+
- - "~>"
|
|
32
31
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
32
|
+
version: '1.8'
|
|
34
33
|
type: :runtime
|
|
35
34
|
prerelease: false
|
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
36
|
requirements:
|
|
38
|
-
- -
|
|
37
|
+
- - "~>"
|
|
39
38
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
39
|
+
version: '1.8'
|
|
41
40
|
- !ruby/object:Gem::Dependency
|
|
42
41
|
name: rspec
|
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,19 +52,19 @@ dependencies:
|
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: 3.13.0
|
|
55
54
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
55
|
+
name: aws-sdk-s3
|
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
|
58
57
|
requirements:
|
|
59
58
|
- - "~>"
|
|
60
59
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 1
|
|
60
|
+
version: '1'
|
|
62
61
|
type: :development
|
|
63
62
|
prerelease: false
|
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
64
|
requirements:
|
|
66
65
|
- - "~>"
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: 1
|
|
67
|
+
version: '1'
|
|
69
68
|
description: The remote_translation_loader gem allows you to fetch YAML translation
|
|
70
69
|
files from remote sources and dynamically load them into your Rails application’s
|
|
71
70
|
I18n translations without writing them to local files.
|
|
@@ -81,13 +80,18 @@ files:
|
|
|
81
80
|
- README.md
|
|
82
81
|
- Rakefile
|
|
83
82
|
- lib/remote_translation_loader.rb
|
|
83
|
+
- lib/remote_translation_loader/configuration.rb
|
|
84
|
+
- lib/remote_translation_loader/errors.rb
|
|
84
85
|
- lib/remote_translation_loader/fetchers/base_fetcher.rb
|
|
85
86
|
- lib/remote_translation_loader/fetchers/file_fetcher.rb
|
|
86
87
|
- lib/remote_translation_loader/fetchers/http_fetcher.rb
|
|
87
88
|
- lib/remote_translation_loader/fetchers/s3_fetcher.rb
|
|
88
89
|
- lib/remote_translation_loader/loader.rb
|
|
90
|
+
- lib/remote_translation_loader/railtie.rb
|
|
91
|
+
- lib/remote_translation_loader/source_resolver.rb
|
|
89
92
|
- lib/remote_translation_loader/tasks/remote_translation_loader.rake
|
|
90
93
|
- lib/remote_translation_loader/version.rb
|
|
94
|
+
- mise.toml
|
|
91
95
|
- sig/remote_translation_loader.rbs
|
|
92
96
|
homepage: https://github.com/gklsan/remote_translation_loader
|
|
93
97
|
licenses:
|
|
@@ -95,8 +99,7 @@ licenses:
|
|
|
95
99
|
metadata:
|
|
96
100
|
homepage_uri: https://github.com/gklsan/remote_translation_loader
|
|
97
101
|
source_code_uri: https://github.com/gklsan/remote_translation_loader
|
|
98
|
-
|
|
99
|
-
post_install_message:
|
|
102
|
+
bug_tracker_uri: https://github.com/gklsan/remote_translation_loader/issues
|
|
100
103
|
rdoc_options: []
|
|
101
104
|
require_paths:
|
|
102
105
|
- lib
|
|
@@ -111,8 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
111
114
|
- !ruby/object:Gem::Version
|
|
112
115
|
version: '0'
|
|
113
116
|
requirements: []
|
|
114
|
-
rubygems_version: 3.
|
|
115
|
-
signing_key:
|
|
117
|
+
rubygems_version: 3.6.6
|
|
116
118
|
specification_version: 4
|
|
117
119
|
summary: Fetches and loads remote YAML translation files into Rails I18n
|
|
118
120
|
test_files: []
|