neeto-translate-cli 0.1.1
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 +7 -0
 - data/README.md +119 -0
 - data/Rakefile +12 -0
 - data/bin/console +11 -0
 - data/bin/setup +8 -0
 - data/exe/neeto-translate-cli +5 -0
 - data/lib/neeto_translate_cli/api.rb +31 -0
 - data/lib/neeto_translate_cli/cli.rb +44 -0
 - data/lib/neeto_translate_cli/payload_builder.rb +119 -0
 - data/lib/neeto_translate_cli/translator.rb +33 -0
 - data/lib/neeto_translate_cli/version.rb +5 -0
 - data/test/fixtures/backend/en.yml +23 -0
 - data/test/fixtures/backend/es.yml +15 -0
 - data/test/fixtures/backend/jp.yml +18 -0
 - data/test/fixtures/frontend/en.json +22 -0
 - data/test/fixtures/frontend/es.json +13 -0
 - data/test/fixtures/frontend/jp.json +19 -0
 - data/test/neeto_translate_cli/payload_builder_test.rb +63 -0
 - data/test/test_helper.rb +6 -0
 - metadata +79 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 7f02f39e18528cfe0511c15be9a69c5f5b156d0ebb7497a6f68c3fc702d70be8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 306076cb3c777e33171135b221f745175ffcd380be3c94e2daabe6dcd334672f
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b28d58ccfd9766d8d7f7660cb3e191856bcb5dcbf6d1f38a025cf925746722802d02c84319cbed03930bba33d35fc55633ca2ebb413daf67120f4eb17fc8bdb8
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 282c29211bb41d7f32c48aec95491f7a7a22373bf90d4866144d3b950f94eb170461a4b85dce698c4b4c6fa266ea01e5048d7ef2c3c6b729c021e28c7756d6a7
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,119 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # neeto-translate-cli
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            A Ruby CLI tool that extracts translation files from Rails applications and makes requests to the neeto-translate API for automated translation management.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ## Table of Contents
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - [Installation](#installation)
         
     | 
| 
      
 8 
     | 
    
         
            +
            - [Usage](#usage)
         
     | 
| 
      
 9 
     | 
    
         
            +
            - [Development](#development)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            ## Installation
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            Install the gem via RubyGems:
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 16 
     | 
    
         
            +
            gem install neeto-translate-cli
         
     | 
| 
      
 17 
     | 
    
         
            +
            ```
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            Or add it to your Gemfile:
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 22 
     | 
    
         
            +
            gem 'neeto-translate-cli'
         
     | 
| 
      
 23 
     | 
    
         
            +
            ```
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            Then run:
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 28 
     | 
    
         
            +
            bundle install
         
     | 
| 
      
 29 
     | 
    
         
            +
            ```
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            ## Usage
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            The `neeto-translate-cli` tool helps you manage translations by:
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            1. Extracting translation keys from your Rails application's frontend (JSON) and backend (YAML) locale files
         
     | 
| 
      
 36 
     | 
    
         
            +
            2. Identifying missing translations across different languages
         
     | 
| 
      
 37 
     | 
    
         
            +
            3. Detecting updated translation keys since the last automated translation
         
     | 
| 
      
 38 
     | 
    
         
            +
            4. Sending translation requests to the neeto-translate API
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            ### Basic Usage
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 43 
     | 
    
         
            +
            neeto-translate-cli [OPTIONS]
         
     | 
| 
      
 44 
     | 
    
         
            +
            ```
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            #### Command line options
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            **Options:**
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            - `--frontend PATH` - Path to frontend locales (default: `app/javascript/src/translations`)
         
     | 
| 
      
 51 
     | 
    
         
            +
            - `--backend PATH` - Path to backend locales (default: `config/locales`)
         
     | 
| 
      
 52 
     | 
    
         
            +
            - `--defaults LANGUAGES` - Comma-separated list of languages to translate (default: `en,fr,de,es`)
         
     | 
| 
      
 53 
     | 
    
         
            +
            - `--repo NAME` - GitHub repository name (e.g., `neetozone/neeto-cal-web`)
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            ### Examples
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            **Custom paths:**
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 60 
     | 
    
         
            +
            neeto-translate-cli --frontend src/locales --backend config/translations
         
     | 
| 
      
 61 
     | 
    
         
            +
            ```
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            **Specific languages:**
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 66 
     | 
    
         
            +
            neeto-translate-cli --defaults "fr,de,es,it"
         
     | 
| 
      
 67 
     | 
    
         
            +
            ```
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            **Full example:**
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 72 
     | 
    
         
            +
            neeto-translate-cli \
         
     | 
| 
      
 73 
     | 
    
         
            +
              --frontend app/javascript/src/translations \
         
     | 
| 
      
 74 
     | 
    
         
            +
              --backend config/locales \
         
     | 
| 
      
 75 
     | 
    
         
            +
              --defaults "fr,de,es" \
         
     | 
| 
      
 76 
     | 
    
         
            +
              --repo "neetozone/neeto-cal-web"
         
     | 
| 
      
 77 
     | 
    
         
            +
            ```
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            ### Environment Variables
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            The following environment variables can be configured:
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            - `NEETO_TRANSLATE_URL` - Base URL for the neeto-translate API (default: `https://translate.neeto.com`)
         
     | 
| 
      
 84 
     | 
    
         
            +
            - `NEETO_TRANSLATE_X_TOKEN` - Authentication token for the API
         
     | 
| 
      
 85 
     | 
    
         
            +
            - `DEFAULT_LANGUAGES` - Comma-separated list of default languages (default: `en,fr,de,es`)
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
            ### How It Works
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
            1. **Extraction**: The tool reads your English translation files (`en.json` for frontend, `en.yml` for backend)
         
     | 
| 
      
 90 
     | 
    
         
            +
            2. **Analysis**: It compares with other language files to identify missing translations
         
     | 
| 
      
 91 
     | 
    
         
            +
            3. **Change Detection**: It detects keys that have been updated since the last automated translation run
         
     | 
| 
      
 92 
     | 
    
         
            +
            4. **API Request**: Sends a payload to the neeto-translate API with:
         
     | 
| 
      
 93 
     | 
    
         
            +
               - Current English translations
         
     | 
| 
      
 94 
     | 
    
         
            +
               - Missing translation keys for each language
         
     | 
| 
      
 95 
     | 
    
         
            +
               - Updated keys that need re-translation
         
     | 
| 
      
 96 
     | 
    
         
            +
               - Repository metadata
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            ## Development
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
            ### Setup
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
            1. Clone the repository:
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 105 
     | 
    
         
            +
            git clone https://github.com/neetozone/neeto-translate.git
         
     | 
| 
      
 106 
     | 
    
         
            +
            cd neeto-translate/cli
         
     | 
| 
      
 107 
     | 
    
         
            +
            ```
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
            2. Install dependencies:
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 112 
     | 
    
         
            +
            bundle install
         
     | 
| 
      
 113 
     | 
    
         
            +
            ```
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            3. Run tests:
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
            ```bash
         
     | 
| 
      
 118 
     | 
    
         
            +
            bundle exec rake test
         
     | 
| 
      
 119 
     | 
    
         
            +
            ```
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/console
    ADDED
    
    | 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "../lib/neeto_translate_cli/cli"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            # You can add fixtures and/or initialization code here to make experimenting
         
     | 
| 
      
 8 
     | 
    
         
            +
            # with your gem easier. You can also use a different console, if you like.
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            require "irb"
         
     | 
| 
      
 11 
     | 
    
         
            +
            IRB.start(__FILE__)
         
     | 
    
        data/bin/setup
    ADDED
    
    
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "faraday"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module NeetoTranslateCli
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Api
         
     | 
| 
      
 8 
     | 
    
         
            +
                def translate!(payload)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  connection.post("/translation") do |request|
         
     | 
| 
      
 10 
     | 
    
         
            +
                    request.body = payload.to_json
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                private
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def connection
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @connection ||= Faraday.new(url: url, headers: headers)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def url
         
     | 
| 
      
 21 
     | 
    
         
            +
                  ENV["NEETO_TRANSLATE_URL"] || CLI::NEETO_TRANSLATE_URL
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def headers
         
     | 
| 
      
 25 
     | 
    
         
            +
                  {
         
     | 
| 
      
 26 
     | 
    
         
            +
                    "Content-Type" => "application/json",
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "NEETO-TRANSLATE-X-TOKEN" => ENV["NEETO_TRANSLATE_X_TOKEN"]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  }
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,44 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "translator"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require "optparse"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module NeetoTranslateCli
         
     | 
| 
      
 8 
     | 
    
         
            +
              class CLI
         
     | 
| 
      
 9 
     | 
    
         
            +
                DEFAULT_LANGUAGES = "en,fr,de,es"
         
     | 
| 
      
 10 
     | 
    
         
            +
                NEETO_TRANSLATE_URL = "https://translate.neeto.com"
         
     | 
| 
      
 11 
     | 
    
         
            +
                DEFAULT_BACKEND_LOCALE_PATH = "config/locales"
         
     | 
| 
      
 12 
     | 
    
         
            +
                DEFAULT_FRONTEND_LOCALE_PATH = "app/javascript/src/translations"
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def self.start(args)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  new(args).run
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def initialize(args)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @args = args
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def run
         
     | 
| 
      
 23 
     | 
    
         
            +
                  options = {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    frontend: DEFAULT_FRONTEND_LOCALE_PATH,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    backend: DEFAULT_BACKEND_LOCALE_PATH,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    default_languages: (ENV["DEFAULT_LANGUAGES"] || DEFAULT_LANGUAGES).to_s.split(","),
         
     | 
| 
      
 27 
     | 
    
         
            +
                    repo: `git rev-parse --show-toplevel`.split("/").last.strip
         
     | 
| 
      
 28 
     | 
    
         
            +
                  }
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  OptionParser.new do |opts|
         
     | 
| 
      
 31 
     | 
    
         
            +
                    opts.on("--frontend PATH", "Path to frontend locales.") { |path| options[:frontend] = path }
         
     | 
| 
      
 32 
     | 
    
         
            +
                    opts.on("--backend PATH", "Path to backend locales") { |path| options[:backend] = path }
         
     | 
| 
      
 33 
     | 
    
         
            +
                    opts.on("--defaults ARRAY", "An array of comma separated languages to be translated") do |languages|
         
     | 
| 
      
 34 
     | 
    
         
            +
                      options[:default_languages] = languages.to_s.split(",")
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                    opts.on("--repo NAME", "Name of the GitHub repository (eg: neetozone/neeto-cal-web).") do |path|
         
     | 
| 
      
 37 
     | 
    
         
            +
                      options[:repo] = path
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end.parse!(@args)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  Translator.new(options).process!
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,119 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "yaml"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module NeetoTranslateCli
         
     | 
| 
      
 5 
     | 
    
         
            +
              class PayloadBuilder
         
     | 
| 
      
 6 
     | 
    
         
            +
                COMMIT_AUTHOR = "neetobot"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :options, :updated_keys
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 12 
     | 
    
         
            +
                  commit_id = `git log --author="#{COMMIT_AUTHOR}" --grep="\[neeto-translate\]" -n 1 --pretty=format:"%H"`.strip
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @updated_keys = commit_id.empty? ? { frontend: [], backend: [] } : find_updated_keys(commit_id)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def process!
         
     | 
| 
      
 17 
     | 
    
         
            +
                  {
         
     | 
| 
      
 18 
     | 
    
         
            +
                    frontend: { en: en_json_flattened, missing_keys: find_missing_keys_for(:frontend) },
         
     | 
| 
      
 19 
     | 
    
         
            +
                    backend: { en: en_yml_flattened, missing_keys: find_missing_keys_for(:backend) },
         
     | 
| 
      
 20 
     | 
    
         
            +
                    metadata: {
         
     | 
| 
      
 21 
     | 
    
         
            +
                      repo: options[:repo],
         
     | 
| 
      
 22 
     | 
    
         
            +
                      frontend: options[:frontend],
         
     | 
| 
      
 23 
     | 
    
         
            +
                      backend: options[:backend]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    }
         
     | 
| 
      
 25 
     | 
    
         
            +
                  }
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def no_new_changes?(payload)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  payload[:frontend][:missing_keys].values.all?(&:empty?) && payload[:backend][:missing_keys].values.all?(&:empty?)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                private
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def en_json_flattened
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @_en_json_flattened ||= flatten_hash(load_json(File.expand_path("en.json", options[:frontend])))
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def en_yml_flattened
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @_en_yml_flattened ||= flatten_hash(load_yml(File.expand_path("en.yml", options[:backend]))["en"])
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def find_updated_keys(commit_id)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  previous_en_json_content = `git show #{commit_id}:#{options[:frontend]}/en.json`
         
     | 
| 
      
 44 
     | 
    
         
            +
                  updated_frontend_keys = []
         
     | 
| 
      
 45 
     | 
    
         
            +
                  unless previous_en_json_content.empty?
         
     | 
| 
      
 46 
     | 
    
         
            +
                    previous_en_json = JSON.parse(previous_en_json_content)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    flat_previous_en_json = flatten_hash(previous_en_json)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    flat_current_en_json = en_json_flattened
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    updated_frontend_keys = flat_current_en_json.keys.select do |key|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      flat_previous_en_json.key?(key) && flat_current_en_json[key] != flat_previous_en_json[key]
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  previous_en_yml_content = `git show #{commit_id}:#{options[:backend]}/en.yml`
         
     | 
| 
      
 56 
     | 
    
         
            +
                  updated_backend_keys = []
         
     | 
| 
      
 57 
     | 
    
         
            +
                  unless previous_en_yml_content.empty?
         
     | 
| 
      
 58 
     | 
    
         
            +
                    previous_en_yml = YAML.load(previous_en_yml_content, aliases: true)["en"]
         
     | 
| 
      
 59 
     | 
    
         
            +
                    flat_previous_en_yml = flatten_hash(previous_en_yml)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    flat_current_en_yml = en_yml_flattened
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    updated_backend_keys = flat_current_en_yml.keys.select do |key|
         
     | 
| 
      
 63 
     | 
    
         
            +
                      flat_previous_en_yml.key?(key) && flat_current_en_yml[key] != flat_previous_en_yml[key]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  { frontend: updated_frontend_keys, backend: updated_backend_keys }
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def find_missing_keys_for(type)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  dir = options[type]
         
     | 
| 
      
 72 
     | 
    
         
            +
                  if type == :frontend
         
     | 
| 
      
 73 
     | 
    
         
            +
                    ext = "json"
         
     | 
| 
      
 74 
     | 
    
         
            +
                    base_keys = en_json_flattened.keys
         
     | 
| 
      
 75 
     | 
    
         
            +
                  else
         
     | 
| 
      
 76 
     | 
    
         
            +
                    ext = "yml"
         
     | 
| 
      
 77 
     | 
    
         
            +
                    base_keys = en_yml_flattened.keys
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  languages.each_with_object({}) do |lang, missing|
         
     | 
| 
      
 81 
     | 
    
         
            +
                    file_path = File.expand_path("#{lang}.#{ext}", dir)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    if File.file?(file_path)
         
     | 
| 
      
 83 
     | 
    
         
            +
                      translations = ext == "json" ? load_json(file_path) : load_yml(file_path)[lang]
         
     | 
| 
      
 84 
     | 
    
         
            +
                      mk = base_keys - flatten_hash(translations).keys
         
     | 
| 
      
 85 
     | 
    
         
            +
                    else
         
     | 
| 
      
 86 
     | 
    
         
            +
                      mk = base_keys
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    missing[lang] = (mk + updated_keys[type]).uniq
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def load_json(file_path)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  JSON.parse(File.read(file_path, encoding: "utf-8"))
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def load_yml(file_path)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  YAML.load_file(file_path, aliases: true)
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def flatten_hash(hash, prefix = nil)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  hash.each_with_object({}) do |(key, value), result|
         
     | 
| 
      
 103 
     | 
    
         
            +
                    current_key = prefix ? "#{prefix}.#{key}" : key.to_s
         
     | 
| 
      
 104 
     | 
    
         
            +
                    if value.is_a?(Hash)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      result.merge!(flatten_hash(value, current_key))
         
     | 
| 
      
 106 
     | 
    
         
            +
                    else
         
     | 
| 
      
 107 
     | 
    
         
            +
                      result[current_key] = value
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                def languages
         
     | 
| 
      
 113 
     | 
    
         
            +
                  default_languages = options[:default_languages]
         
     | 
| 
      
 114 
     | 
    
         
            +
                  project_languages = Dir.glob(File.join(options[:frontend], "*.json")).map { |f| File.basename(f, ".json") }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  (default_languages + project_languages).uniq - ["en"]
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
              end
         
     | 
| 
      
 119 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative "api"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "payload_builder"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module NeetoTranslateCli
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Translator
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :api, :options, :job_id
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @api = NeetoTranslateCli::Api.new
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def process!
         
     | 
| 
      
 16 
     | 
    
         
            +
                  translate!
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                private
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def translate!
         
     | 
| 
      
 22 
     | 
    
         
            +
                  payload_builder = PayloadBuilder.new(options)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  payload = payload_builder.process!
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  if payload_builder.no_new_changes?(payload)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    puts "Nothing to see here. You are all good."
         
     | 
| 
      
 27 
     | 
    
         
            +
                  else
         
     | 
| 
      
 28 
     | 
    
         
            +
                    response = api.translate! payload
         
     | 
| 
      
 29 
     | 
    
         
            +
                    puts response.body
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            en:
         
     | 
| 
      
 2 
     | 
    
         
            +
              common:
         
     | 
| 
      
 3 
     | 
    
         
            +
                success: "Success"
         
     | 
| 
      
 4 
     | 
    
         
            +
                error: "An error occurred"
         
     | 
| 
      
 5 
     | 
    
         
            +
                not_found: "Not Found"
         
     | 
| 
      
 6 
     | 
    
         
            +
              activerecord:
         
     | 
| 
      
 7 
     | 
    
         
            +
                models:
         
     | 
| 
      
 8 
     | 
    
         
            +
                  user: "User"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  product: "Product"
         
     | 
| 
      
 10 
     | 
    
         
            +
                attributes:
         
     | 
| 
      
 11 
     | 
    
         
            +
                  user:
         
     | 
| 
      
 12 
     | 
    
         
            +
                    name: "Name"
         
     | 
| 
      
 13 
     | 
    
         
            +
                    email: "Email"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  product:
         
     | 
| 
      
 15 
     | 
    
         
            +
                    name: "Product Name"
         
     | 
| 
      
 16 
     | 
    
         
            +
                    price: "Price"
         
     | 
| 
      
 17 
     | 
    
         
            +
              mailer:
         
     | 
| 
      
 18 
     | 
    
         
            +
                welcome:
         
     | 
| 
      
 19 
     | 
    
         
            +
                  subject: "Welcome to our service!"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  body: "Hi %{name}, welcome!"
         
     | 
| 
      
 21 
     | 
    
         
            +
                password_reset:
         
     | 
| 
      
 22 
     | 
    
         
            +
                  subject: "Password Reset Request"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  body: "Please click the link to reset your password."
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            es:
         
     | 
| 
      
 2 
     | 
    
         
            +
              common:
         
     | 
| 
      
 3 
     | 
    
         
            +
                success: "Éxito"
         
     | 
| 
      
 4 
     | 
    
         
            +
                error: "Ocurrió un error"
         
     | 
| 
      
 5 
     | 
    
         
            +
              activerecord:
         
     | 
| 
      
 6 
     | 
    
         
            +
                models:
         
     | 
| 
      
 7 
     | 
    
         
            +
                  user: "Usuario"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  product: "Producto"
         
     | 
| 
      
 9 
     | 
    
         
            +
                attributes:
         
     | 
| 
      
 10 
     | 
    
         
            +
                  product:
         
     | 
| 
      
 11 
     | 
    
         
            +
                    name: "Nombre del Producto"
         
     | 
| 
      
 12 
     | 
    
         
            +
              mailer:
         
     | 
| 
      
 13 
     | 
    
         
            +
                welcome:
         
     | 
| 
      
 14 
     | 
    
         
            +
                  subject: "¡Bienvenido a nuestro servicio!"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  body: "Hola %{name}, ¡bienvenido!"
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            jp:
         
     | 
| 
      
 2 
     | 
    
         
            +
              common:
         
     | 
| 
      
 3 
     | 
    
         
            +
                success: "成功"
         
     | 
| 
      
 4 
     | 
    
         
            +
                not_found: "見つかりません"
         
     | 
| 
      
 5 
     | 
    
         
            +
              activerecord:
         
     | 
| 
      
 6 
     | 
    
         
            +
                models:
         
     | 
| 
      
 7 
     | 
    
         
            +
                  user: "ユーザー"
         
     | 
| 
      
 8 
     | 
    
         
            +
                attributes:
         
     | 
| 
      
 9 
     | 
    
         
            +
                  user:
         
     | 
| 
      
 10 
     | 
    
         
            +
                    name: "名前"
         
     | 
| 
      
 11 
     | 
    
         
            +
                    email: "メールアドレス"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  product:
         
     | 
| 
      
 13 
     | 
    
         
            +
                    name: "製品名"
         
     | 
| 
      
 14 
     | 
    
         
            +
                    price: "価格"
         
     | 
| 
      
 15 
     | 
    
         
            +
              mailer:
         
     | 
| 
      
 16 
     | 
    
         
            +
                password_reset:
         
     | 
| 
      
 17 
     | 
    
         
            +
                  subject: "パスワードリセットリクエスト"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  body: "リンクをクリックしてパスワードをリセットしてください。"
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
              "common": {
         
     | 
| 
      
 3 
     | 
    
         
            +
                "ok": "OK",
         
     | 
| 
      
 4 
     | 
    
         
            +
                "cancel": "Cancel",
         
     | 
| 
      
 5 
     | 
    
         
            +
                "submit": "Submit"
         
     | 
| 
      
 6 
     | 
    
         
            +
              },
         
     | 
| 
      
 7 
     | 
    
         
            +
              "login": {
         
     | 
| 
      
 8 
     | 
    
         
            +
                "title": "Login",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "email": "Email Address",
         
     | 
| 
      
 10 
     | 
    
         
            +
                "password": "Password",
         
     | 
| 
      
 11 
     | 
    
         
            +
                "forgot_password": "Forgot your password?"
         
     | 
| 
      
 12 
     | 
    
         
            +
              },
         
     | 
| 
      
 13 
     | 
    
         
            +
              "dashboard": {
         
     | 
| 
      
 14 
     | 
    
         
            +
                "title": "Dashboard",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "welcome_message": "Welcome, {{name,anyCase}}",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "stats": {
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "users": "Users",
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "revenue": "Revenue",
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "orders": "Orders"
         
     | 
| 
      
 20 
     | 
    
         
            +
                }
         
     | 
| 
      
 21 
     | 
    
         
            +
              }
         
     | 
| 
      
 22 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {
         
     | 
| 
      
 2 
     | 
    
         
            +
              "common": {
         
     | 
| 
      
 3 
     | 
    
         
            +
                "ok": "はい",
         
     | 
| 
      
 4 
     | 
    
         
            +
                "cancel": "キャンセル"
         
     | 
| 
      
 5 
     | 
    
         
            +
              },
         
     | 
| 
      
 6 
     | 
    
         
            +
              "login": {
         
     | 
| 
      
 7 
     | 
    
         
            +
                "title": "ログイン",
         
     | 
| 
      
 8 
     | 
    
         
            +
                "email": "メールアドレス",
         
     | 
| 
      
 9 
     | 
    
         
            +
                "password": "パスワード",
         
     | 
| 
      
 10 
     | 
    
         
            +
                "forgot_password": "パスワードをお忘れですか?"
         
     | 
| 
      
 11 
     | 
    
         
            +
              },
         
     | 
| 
      
 12 
     | 
    
         
            +
              "dashboard": {
         
     | 
| 
      
 13 
     | 
    
         
            +
                "title": "ダッシュボード",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "stats": {
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "users": "ユーザー",
         
     | 
| 
      
 16 
     | 
    
         
            +
                  "orders": "注文"
         
     | 
| 
      
 17 
     | 
    
         
            +
                }
         
     | 
| 
      
 18 
     | 
    
         
            +
              }
         
     | 
| 
      
 19 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "test_helper"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class NeetoTranslateCli::PayloadBuilderTest < Minitest::Test
         
     | 
| 
      
 4 
     | 
    
         
            +
              def setup
         
     | 
| 
      
 5 
     | 
    
         
            +
                @options = {
         
     | 
| 
      
 6 
     | 
    
         
            +
                  frontend: "test/fixtures/frontend",
         
     | 
| 
      
 7 
     | 
    
         
            +
                  backend: "test/fixtures/backend",
         
     | 
| 
      
 8 
     | 
    
         
            +
                  default_languages: %w[es de]
         
     | 
| 
      
 9 
     | 
    
         
            +
                }
         
     | 
| 
      
 10 
     | 
    
         
            +
                @payload_builder = NeetoTranslateCli::PayloadBuilder.new(@options)
         
     | 
| 
      
 11 
     | 
    
         
            +
                @payload = @payload_builder.process!
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def test_correct_frontend_payload_is_constructured
         
     | 
| 
      
 15 
     | 
    
         
            +
                en_json_path = File.expand_path("en.json", @options[:frontend])
         
     | 
| 
      
 16 
     | 
    
         
            +
                en_json = JSON.parse(File.read(en_json_path, encoding: "utf-8"))
         
     | 
| 
      
 17 
     | 
    
         
            +
                es_missing_keys = [
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "common.cancel",
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "dashboard.stats.orders",
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "dashboard.stats.revenue",
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "dashboard.stats.users",
         
     | 
| 
      
 22 
     | 
    
         
            +
                  "login.email",
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "login.forgot_password",
         
     | 
| 
      
 24 
     | 
    
         
            +
                  "login.password"
         
     | 
| 
      
 25 
     | 
    
         
            +
                ]
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                assert @payload[:frontend]
         
     | 
| 
      
 28 
     | 
    
         
            +
                assert_equal flatten_hash(en_json), @payload[:frontend][:en]
         
     | 
| 
      
 29 
     | 
    
         
            +
                assert_equal %w[es de jp], @payload_builder.send(:languages)
         
     | 
| 
      
 30 
     | 
    
         
            +
                assert_equal es_missing_keys, @payload.dig(:frontend, :missing_keys, "es").sort
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              def test_correct_backend_payload_is_constructured
         
     | 
| 
      
 34 
     | 
    
         
            +
                en_yml_path = File.expand_path("en.yml", @options[:backend])
         
     | 
| 
      
 35 
     | 
    
         
            +
                  en_yml = YAML.load_file(en_yml_path, aliases: true)["en"]
         
     | 
| 
      
 36 
     | 
    
         
            +
                es_missing_keys = [
         
     | 
| 
      
 37 
     | 
    
         
            +
                  "activerecord.attributes.product.price",
         
     | 
| 
      
 38 
     | 
    
         
            +
                  "activerecord.attributes.user.email",
         
     | 
| 
      
 39 
     | 
    
         
            +
                  "activerecord.attributes.user.name",
         
     | 
| 
      
 40 
     | 
    
         
            +
                  "common.not_found",
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "mailer.password_reset.body",
         
     | 
| 
      
 42 
     | 
    
         
            +
                  "mailer.password_reset.subject"
         
     | 
| 
      
 43 
     | 
    
         
            +
                ]
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                assert @payload[:backend]
         
     | 
| 
      
 46 
     | 
    
         
            +
                assert_equal flatten_hash(en_yml), @payload[:backend][:en]
         
     | 
| 
      
 47 
     | 
    
         
            +
                assert_equal %w[es de jp], @payload_builder.send(:languages)
         
     | 
| 
      
 48 
     | 
    
         
            +
                assert_equal es_missing_keys, @payload.dig(:backend, :missing_keys, "es").sort
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              private
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def flatten_hash(hash, prefix = nil)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  hash.each_with_object({}) do |(key, value), result|
         
     | 
| 
      
 55 
     | 
    
         
            +
                    current_key = prefix ? "#{prefix}.#{key}" : key.to_s
         
     | 
| 
      
 56 
     | 
    
         
            +
                    if value.is_a?(Hash)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      result.merge!(flatten_hash(value, current_key))
         
     | 
| 
      
 58 
     | 
    
         
            +
                    else
         
     | 
| 
      
 59 
     | 
    
         
            +
                      result[current_key] = value
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
    
        data/test/test_helper.rb
    ADDED
    
    
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: neeto-translate-cli
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.1
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Abhay V Ashokan
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire:
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: exe
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2025-07-09 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: faraday
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - ">="
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 27 
     | 
    
         
            +
            description: neeto-translate CLI tool to extract translation files and make requests
         
     | 
| 
      
 28 
     | 
    
         
            +
              to the neeto-translate API
         
     | 
| 
      
 29 
     | 
    
         
            +
            email:
         
     | 
| 
      
 30 
     | 
    
         
            +
            - abhay.ashokan@bigbinary.com
         
     | 
| 
      
 31 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 32 
     | 
    
         
            +
            - neeto-translate-cli
         
     | 
| 
      
 33 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 34 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 35 
     | 
    
         
            +
            files:
         
     | 
| 
      
 36 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 37 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 38 
     | 
    
         
            +
            - bin/console
         
     | 
| 
      
 39 
     | 
    
         
            +
            - bin/setup
         
     | 
| 
      
 40 
     | 
    
         
            +
            - exe/neeto-translate-cli
         
     | 
| 
      
 41 
     | 
    
         
            +
            - lib/neeto_translate_cli/api.rb
         
     | 
| 
      
 42 
     | 
    
         
            +
            - lib/neeto_translate_cli/cli.rb
         
     | 
| 
      
 43 
     | 
    
         
            +
            - lib/neeto_translate_cli/payload_builder.rb
         
     | 
| 
      
 44 
     | 
    
         
            +
            - lib/neeto_translate_cli/translator.rb
         
     | 
| 
      
 45 
     | 
    
         
            +
            - lib/neeto_translate_cli/version.rb
         
     | 
| 
      
 46 
     | 
    
         
            +
            - test/fixtures/backend/en.yml
         
     | 
| 
      
 47 
     | 
    
         
            +
            - test/fixtures/backend/es.yml
         
     | 
| 
      
 48 
     | 
    
         
            +
            - test/fixtures/backend/jp.yml
         
     | 
| 
      
 49 
     | 
    
         
            +
            - test/fixtures/frontend/en.json
         
     | 
| 
      
 50 
     | 
    
         
            +
            - test/fixtures/frontend/es.json
         
     | 
| 
      
 51 
     | 
    
         
            +
            - test/fixtures/frontend/jp.json
         
     | 
| 
      
 52 
     | 
    
         
            +
            - test/neeto_translate_cli/payload_builder_test.rb
         
     | 
| 
      
 53 
     | 
    
         
            +
            - test/test_helper.rb
         
     | 
| 
      
 54 
     | 
    
         
            +
            homepage: https://github.com/neetozone/neeto-translate
         
     | 
| 
      
 55 
     | 
    
         
            +
            licenses: []
         
     | 
| 
      
 56 
     | 
    
         
            +
            metadata:
         
     | 
| 
      
 57 
     | 
    
         
            +
              homepage_uri: https://github.com/neetozone/neeto-translate
         
     | 
| 
      
 58 
     | 
    
         
            +
              source_code_uri: https://github.com/neetozone/neeto-translate
         
     | 
| 
      
 59 
     | 
    
         
            +
            post_install_message:
         
     | 
| 
      
 60 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 61 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 62 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 63 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 64 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 65 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 66 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 67 
     | 
    
         
            +
                  version: 3.1.0
         
     | 
| 
      
 68 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 69 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 70 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 71 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 72 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 73 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 74 
     | 
    
         
            +
            rubygems_version: 3.5.16
         
     | 
| 
      
 75 
     | 
    
         
            +
            signing_key:
         
     | 
| 
      
 76 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 77 
     | 
    
         
            +
            summary: neeto-translate CLI tool to extract translation files and make requests to
         
     | 
| 
      
 78 
     | 
    
         
            +
              the neeto-translate API
         
     | 
| 
      
 79 
     | 
    
         
            +
            test_files: []
         
     |