Class: Multilocale::Sync

Inherits:
Object
  • Object
show all
Defined in:
lib/multilocale/sync.rb

Overview

Moves phrases between multilocale.com and the locale files in a repository.

sync = Multilocale::Sync.new(client: client, config: Multilocale::ConfigFile.discover)
sync.pull   # API   -> config/locales/*.yml
sync.push   # files -> API

Pull is the one to wire into a rake task or CI job; push exists so a developer who edited a locale file by hand can send it back rather than retyping it in the dashboard.

Defined Under Namespace

Classes: Result

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client:, config: nil, project: nil, paths: nil, nested: nil, header: nil, base_dir: nil) ⇒ Sync

Returns a new instance of Sync.



19
20
21
22
23
24
25
26
27
# File 'lib/multilocale/sync.rb', line 19

def initialize(client:, config: nil, project: nil, paths: nil, nested: nil, header: nil, base_dir: nil)
  @client = client
  @config = config
  @project = project
  @paths = paths
  @nested = nested
  @header = header
  @base_dir = base_dir || config&.directory || Dir.pwd
end

Instance Attribute Details

#base_dirObject (readonly)

Returns the value of attribute base_dir.



17
18
19
# File 'lib/multilocale/sync.rb', line 17

def base_dir
  @base_dir
end

#clientObject (readonly)

Returns the value of attribute client.



17
18
19
# File 'lib/multilocale/sync.rb', line 17

def client
  @client
end

#configObject (readonly)

Returns the value of attribute config.



17
18
19
# File 'lib/multilocale/sync.rb', line 17

def config
  @config
end

Instance Method Details

#pathsObject

Raises:



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/multilocale/sync.rb', line 37

def paths
  value = @paths || config&.paths
  value = Array(value)
  return value unless value.empty?

  raise ConfigurationError, <<~MESSAGE
    No output paths. Pass paths: to Sync.new or add them to #{ConfigFile::FILENAME}:

      { "paths": ["config/locales/#{LocaleFile::PLACEHOLDER}.yml"] }
  MESSAGE
end

#projectObject



29
30
31
32
33
34
35
# File 'lib/multilocale/sync.rb', line 29

def project
  @project || config&.project || client.project ||
    raise(ConfigurationError, <<~MESSAGE)
      No project. Pass project: to Sync.new, set "projectId" in #{ConfigFile::FILENAME},
      or export MULTILOCALE_PROJECT.
    MESSAGE
end

#pull(languages: nil) ⇒ Object

Downloads every phrase of the project and rewrites the locale files.

Locales the project declares but has no phrases for are reported in Result#empty_locales and left alone: writing an empty file for them would make i18n treat a missing translation as an empty string.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/multilocale/sync.rb', line 54

def pull(languages: nil)
  # Local configuration is validated before the first request: a missing
  # path template should not cost a round trip to find out about.
  writers

  resolved = resolve_project
  rows = client.phrases.list(project: resolved.name)
  dictionaries = Dictionary.from_phrases(rows)

  wanted = languages || config&.locales || resolved.locales
  wanted = dictionaries.keys if wanted.nil? || wanted.empty?

  files = []
  empty = []

  wanted.sort.each do |language|
    dictionary = dictionaries[language]

    if dictionary.nil? || dictionary.empty?
      empty << language
      next
    end

    writers.each { |writer| files << writer.write(dictionary) }
  end

  Result.new(
    project: resolved,
    files: files,
    languages: wanted.sort - empty,
    phrases: rows.size,
    empty_locales: empty
  )
end

#push(languages: nil) ⇒ Object

Reads the locale files back and upserts them as phrase rows.

Every row is sent with projects: [name]; a row created without it exists in the organization but belongs to no project, and no download will ever include it again.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/multilocale/sync.rb', line 94

def push(languages: nil)
  resolved = resolve_project
  writer = writers.first
  wanted = languages || config&.locales || resolved.locales

  rows = wanted.flat_map do |language|
    dictionary = writer.read(language)
    next [] if dictionary.nil?

    dictionary.map do |key, value|
      { key: key, value: value, language: language, projects: [resolved.name] }
    end
  end

  client.phrases.upsert(rows)
end

#resolve_projectObject



111
112
113
114
115
116
117
118
119
# File 'lib/multilocale/sync.rb', line 111

def resolve_project
  client.projects.find(project)
rescue NotFoundError
  raise NotFoundError.new(
    "No project #{project.inspect} in this organization. `multilocale-ruby projects` lists the ones " \
    "this credential can see.",
    status: 404
  )
end