Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,13 @@ Gemspec/DevelopmentDependencies:
Style/FrozenStringLiteralComment:
Enabled: false

# Request takes its collaborators as keyword arguments -- values, from:, to:,
# provider:, config: and the provider's own options. The confusion this cop
# guards against is positional: six named arguments at a call site read
# perfectly well, and the alternative is pulling them out of an options hash
# where nothing documents them.
# Translator#initialize takes its collaborators as keyword arguments -- values, from:, to:, provider:, config:,
# assume_supported: and the provider's own options -- which read perfectly well named at a call site, unlike
# pulling them out of an undocumented options hash, which is the confusion this cop guards against.
Metrics/ParameterLists:
CountKeywordArgs: false

Metrics/ClassLength:
Exclude:
- lib/translation_diff/tokenizer.rb
- lib/translation_diff/request.rb
# Test classes are mostly tables of cases.
- test/**/*
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,45 @@ All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Breaking

- **Language validation is on by default.** `TranslationDiff.translate` now
refuses, before making a request, any source/target pair the shipped data
doesn't list for that provider -- raising
`TranslationDiff::UnsupportedLanguageError`. Data ships for DeepL, Google,
Azure and ModernMT only; Amazon and LibreTranslate ship none, and a
provider with no shipped data refuses nothing. Two escapes: pass
`assume_supported: true` for one call, or set
`config.validate_languages = false` globally. See
[Languages](docs/languages.md).

### Added

- A `usage` instrumentation event, firing once per provider request, beside
`translate`, `cache`, `request` and `rate_limit`. Its payload carries
`provider`, `characters` (what this library sent, counted locally),
`billed_characters` (what the provider said it charged, or `nil`),
`reported` (whether the provider reports billing **at all** -- not that
this response was billed) and `model`. Summing `billed_characters` across
providers without checking `reported` first produces a total that is
quietly too low: only three of the six built-in providers report billing
at all. See [Instrumentation](docs/instrumentation.md).

### Security

- `Configuration#inspect` and `Provider#inspect` print `[FILTERED]` in place
of every credential option's value, instead of the credential itself. The
filtered set is derived, not hand-maintained: option names matching a
sensitive pattern, plus whatever each registered provider declares in
`sensitive_options`. A non-credential option -- a base URL, a region,
`cache_namespace` -- stays visible in full. A URL-valued option that
carries a credential in its userinfo, `redis_url` included, has just that
part redacted (`rediss://default:[FILTERED]@cache.example.upstash.io:6379`);
the scheme, host, port and path stay visible. See
[Providers](docs/providers.md).

## [3.1.0] - 2026-09-08

First release under the name **translation_diff**. This gem was published as
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ TranslationDiff.translate(blog_post, from: "en", to: "de", provider: :google)
```

```ruby
# Any keyword other than from:, to:, provider: and config: is forwarded to the provider
# Any keyword other than from:, to:, provider:, config: and assume_supported: is forwarded to
# the provider. assume_supported: is this library's own decision, not a vendor's, so it's
# reserved rather than forwarded -- it must never reach a payload.
TranslationDiff.translate(contract, from: "en", to: "de", formality: :more)
```

Expand Down Expand Up @@ -138,7 +140,7 @@ This gem loads `ox`, `pragmatic_segmenter`, `faraday`, and `faraday-retry` at re

## Documentation

[Configuration](docs/configuration.md) · [Providers](docs/providers.md) · [Caching](docs/caching.md) · [Contracts](docs/contracts.md) · [Instrumentation](docs/instrumentation.md) · [Errors](docs/errors.md) · [How it works](docs/how-it-works.md) · [Upgrading & development](docs/development.md)
[Configuration](docs/configuration.md) · [Providers](docs/providers.md) · [Languages](docs/languages.md) · [Caching](docs/caching.md) · [Contracts](docs/contracts.md) · [Instrumentation](docs/instrumentation.md) · [Errors](docs/errors.md) · [How it works](docs/how-it-works.md) · [Upgrading & development](docs/development.md)

## Contributing

Expand Down
26 changes: 26 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,29 @@ Rake::TestTask.new(:test) do |t|
end

task default: :test

namespace :languages do
desc "Re-fetch every provider's language lists from its vendor"
task :refresh do
require "translation_diff"

not_shipped = TranslationDiff::Languages::NOT_SHIPPED
unless not_shipped.empty?
puts "not shipping data for #{not_shipped.join(', ')}: a vendor credential or a private instance " \
"would make the file unshareable"
end

skip = [:null, *not_shipped]
providers = TranslationDiff::Providers.names.reject { |name| skip.include?(name) }.map do |name|
TranslationDiff::Providers.build(name, TranslationDiff.config)
rescue TranslationDiff::ConfigurationError => e
warn "skipping #{name}: #{e.message}"
nil
end.compact

report = TranslationDiff::Languages::Refresh.call(providers: providers)
puts "updated: #{report[:updated].join(', ')}" unless report[:updated].empty?
puts "skipped: #{report[:skipped].join(', ')}" unless report[:skipped].empty?
report[:failed].each { |name, message| warn "failed: #{name}: #{message}" }
end
end
Loading
Loading