i18n-keyless-rails 3.5.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.
data/llms.txt ADDED
@@ -0,0 +1,101 @@
1
+ # i18n-keyless-rails
2
+
3
+ > Keyless translations for Ruby on Rails 7 and 8. `t('Welcome to our app')` (the source string where a key would go) resolves through the i18n-keyless API: AI translation on the first miss, cached in `Rails.cache`, served from there. One gem, two `.env` lines, zero code change for the strings that already read as text.
4
+
5
+ This file is the whole gem documentation as one pasteable Markdown page. The general i18n-keyless documentation (dashboard, MCP server, the JavaScript SDKs) is at https://docs.i18n-keyless.com/llms.txt.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ bundle add i18n-keyless-rails
11
+ ```
12
+
13
+ ```dotenv
14
+ I18N_KEYLESS_API_KEY=your-key
15
+ I18N_KEYLESS_LANGUAGES=en,fr,es # required for translation: every language the app serves
16
+ I18N_KEYLESS_PRIMARY_LANG=en # optional, default I18n.default_locale
17
+ ```
18
+
19
+ Requirements: Ruby >= 3.1, Rails 7.0 to 8.x (or any Ruby app with `i18n` and `activesupport`). The Railtie `I18nKeyless::Railtie` is auto-loaded.
20
+
21
+ ## Use
22
+
23
+ ```ruby
24
+ t('Welcome to our app') # a source string: keyless
25
+ t('Welcome %{name}', name: user.name) # I18n placeholders
26
+ t('8 heures', context: 'duration') # context: stored as "8 heures__duration"
27
+ i18nk('8 heures', context: 'clock time') # the helper (views, controllers, mailers, jobs)
28
+ i18nk('Payer', namespace: 'checkout') # an i18n-keyless namespace
29
+ i18nk('close') # a lowercase word: the helper never reads it as a key
30
+ t('users.index.title') # a Rails key: YAML as before, never sent
31
+ I18n.locale = :"pt-BR" # Rails locales are mapped (pt-BR)
32
+ I18nKeyless.t('Bonjour', locale: :en) # outside a view
33
+ ```
34
+
35
+ `i18nk(text, values = nil, context: nil, locale: nil, namespace: nil, **values)`.
36
+
37
+ Rails-key rule (`I18nKeyless.keyless_key?`, `config.rails_key_pattern` = `/\A[a-z0-9_]+(\.[a-z0-9_]+)*\z/`): a Symbol, a `scope:`, or a lowercase identifier path is a Rails key; anything with a space, an uppercase letter or punctuation is a source string.
38
+
39
+ ## Behaviour
40
+
41
+ - Primary locale: the source string is returned, no API call.
42
+ - The backend is `I18n::Backend::Chain.new(<the app's backend>, I18nKeyless::Backend.new)`: a YAML line wins.
43
+ - Other locale, first miss in the process: the locale's dictionary is read from the cache; absent, it is fetched once from `GET {api_url}/translate/{lang}?last_refresh=` and stored, then kept in the process.
44
+ - Missing string: source text returned (placeholders replaced), miss recorded. After the response (`I18nKeyless::Middleware`, a `Rack::BodyProxy`), after each ActiveJob, or as an `I18nKeyless::TranslateMissingKeysJob` when `queue` is set, misses are sent to `POST {api_url}/translate` (body `{key, context?, namespace?, languages, primaryLanguage}`, where `languages` is the configured list plus the primary, never the locale that missed), deduplicated by key and context, 30 in flight at most. Answers are merged into the cache. With an empty `languages` config nothing is sent (the API stores the received list as the project's languages): the source text is served and one warning is logged per process.
45
+ - Stale dictionary (older than `cache_ttl`): served, then revalidated after the response with `If-None-Match: <etag>`; `304` keeps it.
46
+ - HTTP policy: 10 s timeout, retries with backoff 500 ms then 1500 ms on network error, timeout, 429, 5xx; no retry on other 4xx; never raises. A failed fetch is remembered 60 s.
47
+ - Headers on every request: `Authorization: Bearer <api_key>`, `Content-Type: application/json`, `Version: 3.5.0`, `sdk: rails` (a server label, counted like `node`). No `unique_id` (a server is counted by its connection).
48
+ - Usage analytics (node SDK rule): the UTC date each string was last served is recorded (namespace, `key__context`), merged into a cumulative map in the cache (never cleared), and POSTed to `{api_url}/translate/last-used-translations` as `{primaryLanguage, translationsUsageByNamespace}` after the response, at most once every 10 s across processes (an `unless_exist` cache lock), only when the map changed. Fire and forget. `usage: false` disables it.
49
+
50
+ ## Configuration (I18nKeyless.configure { |c| ... })
51
+
52
+ | key | env | default |
53
+ | --- | --- | --- |
54
+ | enabled | I18N_KEYLESS_ENABLED | true |
55
+ | api_key | I18N_KEYLESS_API_KEY | nil |
56
+ | api_url | I18N_KEYLESS_API_URL | https://api.i18n-keyless.com |
57
+ | primary | I18N_KEYLESS_PRIMARY_LANG | I18n.default_locale |
58
+ | languages | I18N_KEYLESS_LANGUAGES | empty: required for translation, nothing is sent without it |
59
+ | namespace | I18N_KEYLESS_NAMESPACE | default |
60
+ | cache | | Rails.cache (a MemoryStore outside Rails) |
61
+ | cache_ttl | I18N_KEYLESS_CACHE_TTL | 3600 |
62
+ | cache_prefix | | i18n-keyless |
63
+ | timeout | | 10 |
64
+ | retry | | [500, 1500] |
65
+ | concurrency | | 30 |
66
+ | usage | I18N_KEYLESS_USAGE | true |
67
+ | queue | I18N_KEYLESS_QUEUE | nil |
68
+ | logger | | Rails.logger |
69
+ | rails_key_pattern | | `/\A[a-z0-9_]+(\.[a-z0-9_]+)*\z/` (nil: every string is keyless) |
70
+
71
+ ## Locale mapping
72
+
73
+ Rails locale to i18n-keyless code: exact match first (`pt-BR`, `zh-Hans`), then Chinese by region (`zh_CN`, `zh_SG` to `zh-Hans`; `zh_TW`, `zh_HK`, `zh_MO` to `zh-Hant`), `es-419` to `es-MX`, then the bare language (`fr_FR` to `fr`, `pt-AO` to `pt`). Unknown locales are left alone. 48 codes: ar, bn, ca, zh-Hans, zh-Hant, hr, cs, da, nl, en, en-GB, fi, fr, fr-CA, de, el, gu, he, hi, hu, id, it, ja, kn, ko, ms, ml, mr, no, or, pl, pt, pt-BR, pa, ro, ru, sk, sl, es, es-MX, sv, ta, te, th, tr, uk, ur, vi. List the served locales in `config.i18n.available_locales` when they have no YAML file.
74
+
75
+ ## Limitations
76
+
77
+ - YAML plural rules (`one:` / `other:`) do not apply to a source string. Use `i18nk()` with a `context` per plural form, or a YAML file.
78
+ - A lowercase one-word string (`t('close')`) is read as a Rails key: use `i18nk('close')`.
79
+ - The `_html` suffix convention does not apply to a source string.
80
+ - Misses are deduplicated by key and context (the SDK queue ignores the context).
81
+ - Usage counts only the strings served by this gem: a string served from YAML is not counted.
82
+ - The miss guard and the usage lock live in the cache: a `:memory_store` is per process; use a shared store in production.
83
+ - A source string is capped at 2000 characters (`context` and `namespace` at 200). Long-form content is allowed, but a blog post is one translation **per Markdown block** of about 1000 characters: keep the Markdown inside each block, give every block of the document the same `context` (one very short summary of it) and one `namespace` per document. https://docs.i18n-keyless.com/docs/guides/long-form-content
84
+
85
+ ## Classes
86
+
87
+ - `I18nKeyless`: `configure`, `config`, `translate` / `t`, `flush`, `install!`, `uninstall!`, `keyless_key?`, `translator`, `reset!`.
88
+ - `I18nKeyless::Railtie`: middleware, helper mix-ins, `after_perform` flush, `after_initialize` chain, `at_exit` flush.
89
+ - `I18nKeyless::Backend`: the chained I18n backend (`lookup`, `reload!`).
90
+ - `I18nKeyless::Translator`: `get`, `lookup`, `ensure_loaded`, `flush`, `translate_now`, `pending_misses`, `pending_usage`, `resolve_namespace`.
91
+ - `I18nKeyless::ApiClient`: `fetch_dictionary`, `translate`, `send_usage`, `dictionary_url`, `translate_body`, `decide`, `error_for`; constants `VERSION`, `SDK`, `DEFAULT_URL`.
92
+ - `I18nKeyless::DictionaryStore`: cache entries `{translations, etag, fetched_at, failed}`, `claim_miss` / `release_miss` guard, `usage` / `merge_usage` / `claim_usage_slot`.
93
+ - `I18nKeyless::Locale`: `to_lang`, `resolve`, `lang?`, `to_app_store_locale`, `AVAILABLE_LANGS`.
94
+ - `I18nKeyless::Miss`: `key`, `context`, `namespace`, `langs`, `lookup_key`, `id`.
95
+ - `I18nKeyless::Helper`: `i18nk`. `I18nKeyless::Middleware`: the Rack flush. `I18nKeyless::TranslateMissingKeysJob`: queued misses.
96
+
97
+ ## Links
98
+
99
+ - Get an API key: https://i18n-keyless.com/#get-api-key
100
+ - Dashboard: https://i18n-keyless.com/dashboard
101
+ - Docs: https://docs.i18n-keyless.com
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n-keyless-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Arnaud Ambroselli
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: i18n
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '1.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '1.8'
40
+ description: 'Keyless translations for Rails. Write the source string where a key
41
+ would go, t(''Welcome to our app''), and it resolves through the i18n-keyless API:
42
+ AI translation on the first miss, cached in Rails.cache, served from there. One
43
+ gem, two .env lines, no config/locales/*.yml to maintain by hand.'
44
+ email:
45
+ - arnaud.ambroselli.io@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - LICENSE.md
51
+ - README.md
52
+ - SKILL.md
53
+ - lib/i18n-keyless-rails.rb
54
+ - lib/i18n_keyless.rb
55
+ - lib/i18n_keyless/api_client.rb
56
+ - lib/i18n_keyless/backend.rb
57
+ - lib/i18n_keyless/config.rb
58
+ - lib/i18n_keyless/dictionary_store.rb
59
+ - lib/i18n_keyless/helper.rb
60
+ - lib/i18n_keyless/locale.rb
61
+ - lib/i18n_keyless/middleware.rb
62
+ - lib/i18n_keyless/miss.rb
63
+ - lib/i18n_keyless/railtie.rb
64
+ - lib/i18n_keyless/translate_missing_keys_job.rb
65
+ - lib/i18n_keyless/translator.rb
66
+ - lib/i18n_keyless/version.rb
67
+ - llms.txt
68
+ homepage: https://i18n-keyless.com
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ homepage_uri: https://i18n-keyless.com
73
+ source_code_uri: https://github.com/arnaudambro/i18n-keyless/tree/main/ports/rails
74
+ documentation_uri: https://docs.i18n-keyless.com
75
+ changelog_uri: https://github.com/arnaudambro/i18n-keyless/blob/main/CHANGELOG.md
76
+ rubygems_mfa_required: 'true'
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '3.1'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.6.9
92
+ specification_version: 4
93
+ summary: 'Keyless translations for Ruby on Rails: t(''Welcome to our app'') resolves
94
+ through the i18n-keyless API.'
95
+ test_files: []