123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- module Agents
- class GoogleTranslationAgent < Agent
- cannot_be_scheduled!
- can_dry_run!
- gem_dependency_check do
- require 'google/cloud/translate/v2'
- rescue LoadError
- false
- else
- true
- end
- description <<~MD
- The Translation Agent will attempt to translate text between natural languages.
- #{'## Include `google-api-client` in your Gemfile to use this Agent!' if dependencies_missing?}
- Services are provided using Google Translate. You can [sign up](https://cloud.google.com/translate/) to get `google_api_key` which is required to use this agent.
- The service is **not free**.
- To use credentials for the `google_api_key` use the liquid `credential` tag like so `{% credential google-api-key %}`
- `to` must be filled with a [translator language code](https://cloud.google.com/translate/docs/languages).
- `from` is the language translated from. If it's not specified, the API will attempt to detect the source language automatically and return it within the response.
- Specify an object in `content` field using [Liquid](https://github.com/huginn/huginn/wiki/Formatting-Events-using-Liquid) expressions, which will be evaluated for each incoming event, and then translated to become the payload of the new event.
- You can specify a nested object of any levels containing arrays and objects, and all string values except for object keys will be recursively translated.
- Set `mode` to `merge` if you want to merge each translated content with the original event payload. The default behavior (`clean`) is to emit events with only translated contents.
- `expected_receive_period_in_days` is the maximum number of days you would allow to pass between events.
- MD
- event_description "User defined"
- def default_options
- {
- 'mode' => 'clean',
- 'to' => 'sv',
- 'from' => 'en',
- 'google_api_key' => '',
- 'expected_receive_period_in_days' => 1,
- 'content' => {
- 'text' => "{{message}}",
- 'moretext' => "{{another_message}}"
- }
- }
- end
- def working?
- last_receive_at && last_receive_at > interpolated['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
- end
- def validate_options
- unless options['google_api_key'].present? && options['to'].present? && options['content'].present? && options['expected_receive_period_in_days'].present?
- errors.add :base, "google_api_key, to, content and expected_receive_period_in_days are all required"
- end
- case options['mode'].presence
- when nil, /\A(?:clean|merge)\z|\{/
- # ok
- else
- errors.add(:base, "mode must be 'clean' or 'merge'")
- end
- end
- def receive(incoming_events)
- incoming_events.each do |event|
- interpolate_with(event) do
- translated_content = translate(interpolated['content'])
- case interpolated['mode']
- when 'merge'
- create_event payload: event.payload.merge(translated_content)
- else
- create_event payload: translated_content
- end
- end
- end
- end
- def translate(content)
- if !content.is_a?(Hash)
- error("content must be an object, but it is #{content.class}.")
- return
- end
- api = Google::Cloud::Translate::V2.new(
- key: interpolated['google_api_key']
- )
- texts = []
- walker = ->(value) {
- case value
- in nil | Numeric | true | false
- in _ if _.blank?
- in String
- texts << value
- in Array
- value.each(&walker)
- in Hash
- value.each_value(&walker)
- end
- }
- walker.call(content)
- translations =
- if texts.empty?
- []
- else
- api.translate(
- *texts,
- from: interpolated['from'].presence,
- to: interpolated['to'],
- format: 'text',
- )
- end
- # Hash key order should be constant in Ruby
- mapper = ->(value) {
- case value
- in nil | Numeric | true | false
- value
- in _ if _.blank?
- value
- in String
- translations&.shift&.text
- in Array
- value.map(&mapper)
- in Hash
- value.transform_values(&mapper)
- end
- }
- mapper.call(content)
- end
- end
- end
|