Class: Multilocale::LocaleFile
- Inherits:
-
Object
- Object
- Multilocale::LocaleFile
- Defined in:
- lib/multilocale/locale_file.rb
Overview
Reads and writes the locale files on disk.
The path is a template containing %lang%, the same convention the npm CLI
uses in multilocale.json:
config/locales/%lang%.yml -> config/locales/en.yml, config/locales/es.yml, …
YAML is written the way the i18n gem expects it: a single top-level locale
key, nested below it. Two differences from npx multilocale download are
deliberate and both matter to Ruby:
* no injected "locale" entry. The CLI writes one into every dictionary it
generates (only its Swift writer strips it again); in a Rails locale
file it would surface as the translation `t("locale")`.
* dotted keys are nested, because i18n resolves them by walking hashes.
Constant Summary collapse
- FORMATS =
%i[yaml json].freeze
- PLACEHOLDER =
"%lang%"
Instance Attribute Summary collapse
-
#base_dir ⇒ Object
readonly
Returns the value of attribute base_dir.
-
#format ⇒ Object
readonly
Returns the value of attribute format.
-
#header ⇒ Object
readonly
Returns the value of attribute header.
-
#nested ⇒ Object
readonly
Returns the value of attribute nested.
-
#path_template ⇒ Object
readonly
Returns the value of attribute path_template.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(path_template:, format: nil, nested: true, header: nil, base_dir: Dir.pwd) ⇒ LocaleFile
constructor
A new instance of LocaleFile.
- #path_for(language) ⇒ Object
-
#read(language) ⇒ Object
Reads one language file back into a Dictionary, flattening the nesting.
- #render(dictionary) ⇒ Object
-
#write(dictionary) ⇒ Object
Writes one language and returns the absolute path written.
Constructor Details
#initialize(path_template:, format: nil, nested: true, header: nil, base_dir: Dir.pwd) ⇒ LocaleFile
Returns a new instance of LocaleFile.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/multilocale/locale_file.rb', line 29 def initialize(path_template:, format: nil, nested: true, header: nil, base_dir: Dir.pwd) unless path_template.to_s.include?(PLACEHOLDER) raise ConfigurationError, "Path #{path_template.inspect} must contain #{PLACEHOLDER}, e.g. " \ "config/locales/#{PLACEHOLDER}.yml" end @path_template = path_template.to_s @format = (format || self.class.infer_format(@path_template)).to_sym unless FORMATS.include?(@format) raise ConfigurationError, "Unsupported format #{@format.inspect} (supported: #{FORMATS.join(', ')}). " \ "The npm CLI's cjs/esm/js/swift writers have no equivalent here." end @nested = nested @header = header @base_dir = base_dir end |
Instance Attribute Details
#base_dir ⇒ Object (readonly)
Returns the value of attribute base_dir.
27 28 29 |
# File 'lib/multilocale/locale_file.rb', line 27 def base_dir @base_dir end |
#format ⇒ Object (readonly)
Returns the value of attribute format.
27 28 29 |
# File 'lib/multilocale/locale_file.rb', line 27 def format @format end |
#header ⇒ Object (readonly)
Returns the value of attribute header.
27 28 29 |
# File 'lib/multilocale/locale_file.rb', line 27 def header @header end |
#nested ⇒ Object (readonly)
Returns the value of attribute nested.
27 28 29 |
# File 'lib/multilocale/locale_file.rb', line 27 def nested @nested end |
#path_template ⇒ Object (readonly)
Returns the value of attribute path_template.
27 28 29 |
# File 'lib/multilocale/locale_file.rb', line 27 def path_template @path_template end |
Class Method Details
.infer_format(path) ⇒ Object
49 50 51 52 53 54 |
# File 'lib/multilocale/locale_file.rb', line 49 def self.infer_format(path) case File.extname(path).downcase when ".json" then :json else :yaml end end |
Instance Method Details
#path_for(language) ⇒ Object
56 57 58 |
# File 'lib/multilocale/locale_file.rb', line 56 def path_for(language) File.(path_template.gsub(PLACEHOLDER, language.to_s), base_dir) end |
#read(language) ⇒ Object
Reads one language file back into a Dictionary, flattening the nesting. Returns nil when the file does not exist.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/multilocale/locale_file.rb', line 85 def read(language) path = path_for(language) return nil unless File.exist?(path) raw = File.read(path) document = case format when :yaml then YAML.safe_load(raw) || {} else JSON.parse(raw) end # A locale file is `{ "en" => { … } }`; anything else is either already # flat or someone else's file, and is taken as it comes. body = if document.is_a?(Hash) && document.size == 1 && document.key?(language.to_s) document[language.to_s] else document end Dictionary.new(language.to_s, Dictionary.flatten(body)) end |
#render(dictionary) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/multilocale/locale_file.rb', line 68 def render(dictionary) body = nested ? dictionary.nested : dictionary.to_h case format when :yaml # line_width: -1 keeps long sentences on one line; wrapped YAML is # valid but re-wraps on every unrelated edit and ruins the diff. "#{yaml_header}#{{ dictionary.language.to_s => body }.to_yaml(line_width: -1)}" else # JSON has no comment syntax, so `header` is deliberately dropped here # rather than written as an invalid first line. "#{JSON.pretty_generate(body)}\n" end end |
#write(dictionary) ⇒ Object
Writes one language and returns the absolute path written.
61 62 63 64 65 66 |
# File 'lib/multilocale/locale_file.rb', line 61 def write(dictionary) path = path_for(dictionary.language) FileUtils.mkdir_p(File.dirname(path)) File.write(path, render(dictionary)) path end |