Skip to content

Commit 5dd4016

Browse files
committed
Initial implementation for Locale models (WIP)
Still requires: - Completing the list of ALL_KNOWN_LOCALES - Enabling from_ios and from_app_store family of tests once we have the values
1 parent e02e57d commit 5dd4016

File tree

3 files changed

+235
-1
lines changed

3 files changed

+235
-1
lines changed

lib/fastlane/plugin/wpmreleasetoolkit.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Fastlane
44
module Wpmreleasetoolkit
55
# Return all .rb files inside the "actions" and "helper" directory
66
def self.all_classes
7-
Dir[File.expand_path('**/{actions,helper}/**/*.rb', File.dirname(__FILE__))]
7+
Dir[File.expand_path('**/{actions,helper,models}/**/*.rb', File.dirname(__FILE__))]
88
end
99
end
1010
end
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
module Fastlane
2+
Locale = Struct.new(:glotpress, :android, :google_play, :ios, :app_store, keyword_init: true) do
3+
def self.[](code)
4+
Locales[code].first
5+
end
6+
end
7+
8+
class Locales
9+
10+
###################
11+
## Constants
12+
ALL_KNOWN_LOCALES = [
13+
Locale.new(glotpress: "ar", android: "ar", google_play: "ar" ),
14+
Locale.new(glotpress: "de", android: "de", google_play: "de-DE"),
15+
Locale.new(glotpress: "en-gb", android: "en-rGB", google_play: "en-US"),
16+
Locale.new(glotpress: "es", android: "es", google_play: "es-ES"),
17+
Locale.new(glotpress: "fr-ca", android: "fr-rCA", google_play: "fr-CA"),
18+
Locale.new(glotpress: "fr", android: "fr", google_play: "fr-FR"),
19+
Locale.new(glotpress: "he", android: "he", google_play: "iw-IL"),
20+
Locale.new(glotpress: "id", android: "id", google_play: "id" ),
21+
Locale.new(glotpress: "it", android: "it", google_play: "it-IT"),
22+
Locale.new(glotpress: "ja", android: "ja", google_play: "ja-JP"),
23+
Locale.new(glotpress: "ko", android: "ko", google_play: "ko-KR"),
24+
Locale.new(glotpress: "nl", android: "nl", google_play: "nl-NL"),
25+
Locale.new(glotpress: "pl", android: "pl", google_play: "pl-PL"),
26+
Locale.new(glotpress: "pt-br", android: "pt-rBR", google_play: "pt-BR"),
27+
Locale.new(glotpress: "ru", android: "ru", google_play: "ru-RU"),
28+
Locale.new(glotpress: "sr", android: "sr", google_play: "sr" ),
29+
Locale.new(glotpress: "sv", android: "sv", google_play: "sv-SE"),
30+
Locale.new(glotpress: "th", android: "th", google_play: "th" ),
31+
Locale.new(glotpress: "tr", android: "tr", google_play: "tr-TR"),
32+
Locale.new(glotpress: "vi", android: "vi", google_play: "vi" ),
33+
Locale.new(glotpress: "zh-cn", android: "zh-rCN", google_play: "zh-CN"),
34+
Locale.new(glotpress: "zh-tw", android: "zh-rTW", google_play: "zh-TW"),
35+
Locale.new(glotpress: "az", android: "az"),
36+
Locale.new(glotpress: "el", android: "el"),
37+
Locale.new(glotpress: "es-mx", android: "es-rMX"),
38+
Locale.new(glotpress: "es-cl", android: "es-rCL"),
39+
Locale.new(glotpress: "gd", android: "gd"),
40+
Locale.new(glotpress: "hi", android: "hi"),
41+
Locale.new(glotpress: "hu", android: "hu"),
42+
Locale.new(glotpress: "nb", android: "nb"),
43+
Locale.new(glotpress: "pl", android: "pl"),
44+
Locale.new(glotpress: "th", android: "th"),
45+
Locale.new(glotpress: "uz", android: "uz"),
46+
Locale.new(glotpress: "zh-tw", android: "zh-rHK"),
47+
Locale.new(glotpress: "eu", android: "eu"),
48+
Locale.new(glotpress: "ro", android: "ro"),
49+
Locale.new(glotpress: "mk", android: "mk"),
50+
Locale.new(glotpress: "en-au", android: "en-rAU"),
51+
Locale.new(glotpress: "sr", android: "sr"),
52+
Locale.new(glotpress: "sk", android: "sk"),
53+
Locale.new(glotpress: "cy", android: "cy"),
54+
Locale.new(glotpress: "da", android: "da"),
55+
Locale.new(glotpress: "bg", android: "bg"),
56+
Locale.new(glotpress: "sq", android: "sq"),
57+
Locale.new(glotpress: "hr", android: "hr"),
58+
Locale.new(glotpress: "cs", android: "cs"),
59+
Locale.new(glotpress: "pt-br", android: "pt-rBR"),
60+
Locale.new(glotpress: "en-ca", android: "en-rCA"),
61+
Locale.new(glotpress: "ms", android: "ms"),
62+
Locale.new(glotpress: "es-ve", android: "es-rVE"),
63+
Locale.new(glotpress: "gl", android: "gl"),
64+
Locale.new(glotpress: "is", android: "is"),
65+
Locale.new(glotpress: "es-co", android: "es-rCO"),
66+
Locale.new(glotpress: "kmr", android: "kmr")
67+
].freeze
68+
69+
MAG16_GP_CODES = %w[ar de es fr he id it ja ko nl pt-br ru sv tr zh-cn zh-tw].freeze
70+
71+
###################
72+
## Static Methods
73+
74+
class << self
75+
76+
# @return [Array<Locale>] Array of all the known locales
77+
#
78+
def all
79+
ALL_KNOWN_LOCALES
80+
end
81+
82+
# Define from_glotpress(code_or_list), from_android(code_or_list) … methods
83+
#
84+
# @param [Array<String>, String] list of locale codes to search for, or single value for single result
85+
# @return [Array<Locale>, Locale] list of found locales (empty if none found), or single locale if a single value was passed (or nil if not found)
86+
#
87+
%i[glotpress android google_play ios app_store].each do |key|
88+
define_method("from_#{key}") { |args| search(key, args) }
89+
end
90+
91+
# Return an Array<Locale> based on glotpress locale codes
92+
#
93+
# @note If you need a single locale, you can use Locale[code] instead of Locales[code]
94+
# @param [String..., Array<String>] Arbitrary list of strings, either passed as a single array parameter, or as a vararg list of params
95+
# @return [Array<Locale>] The found locales.
96+
#
97+
def [](*list)
98+
# If we passed an Array, `*list` will make it an Array<Array<String>>, so taking `list.first` in those cases to go back to Array<String>
99+
list = list.first if list.count == 1 && list.first.is_a?(Array)
100+
from_glotpress(list)
101+
end
102+
103+
# Return the subset of the 16 locales most of our apps are localized 100% (what we call the "Magnificent 16")
104+
#
105+
# @return [Array<Locale>] List of the Mag16 locales
106+
def mag16
107+
from_glotpress(MAG16_GP_CODES)
108+
end
109+
110+
###################
111+
112+
private
113+
114+
# Search the known locales for just the ones having the provided locale code, where the codes are expressed using the standard for the given key
115+
def search(key, code_or_list)
116+
if code_or_list.is_a?(Array)
117+
code_or_list.map { |code| search(key, code) }
118+
else # String
119+
raise 'The locale code should not contain spaces. Did you accidentally use `%[]` instead of `%w[]` at call site?' if code_or_list.include?(' ')
120+
ALL_KNOWN_LOCALES.find { |locale| locale.send(key) == code_or_list } || not_found(code_or_list, key)
121+
end
122+
end
123+
124+
def not_found(code, key)
125+
raise "Unknown locale for #{key} code '#{code}'"
126+
end
127+
end
128+
end
129+
end

spec/locales_spec.rb

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
require 'spec_helper'
2+
3+
describe Fastlane::Locale do
4+
it 'returns a single Locale if one was found' do
5+
locale = Fastlane::Locale['fr']
6+
expect(locale).to be_instance_of(Fastlane::Locale)
7+
expect(locale.glotpress).to eq('fr')
8+
end
9+
10+
it 'raises if no locale was found for a given code' do
11+
expect {
12+
Fastlane::Locale['invalidcode']
13+
}.to raise_error(RuntimeError, "Unknown locale for glotpress code 'invalidcode'")
14+
end
15+
end
16+
17+
describe Fastlane::Locales do
18+
shared_examples 'from_xxx' do |key, fr_code, pt_code|
19+
let(:method_sym) { "from_#{key}".to_sym }
20+
it 'can find a locale from a single code' do
21+
fr_locale = Fastlane::Locales.send(method_sym, fr_code)
22+
expect(fr_locale).to be_instance_of(Fastlane::Locale)
23+
expect(fr_locale.glotpress).to eq('fr')
24+
expect(fr_locale.android).to eq('fr')
25+
expect(fr_locale.google_play).to eq('fr-FR')
26+
end
27+
28+
it 'can find locales from a multiple codes' do
29+
locales = Fastlane::Locales.send(method_sym, [fr_code, pt_code])
30+
expect(locales).to be_instance_of(Array)
31+
32+
expect(locales[0]).to be_instance_of(Fastlane::Locale)
33+
expect(locales[0].glotpress).to eq('fr')
34+
35+
expect(locales[1]).to be_instance_of(Fastlane::Locale)
36+
expect(locales[1].glotpress).to eq('pt-br')
37+
end
38+
39+
it 'raises if one of the locale codes passed was not found' do
40+
expect {
41+
Fastlane::Locales.send(method_sym, [fr_code, 'invalidcode', 'pt-br'])
42+
}.to raise_error(RuntimeError, "Unknown locale for #{key} code 'invalidcode'")
43+
end
44+
end
45+
46+
describe 'from_glotpress' do
47+
include_examples 'from_xxx', :glotpress, 'fr', 'pt-br'
48+
end
49+
50+
describe 'from_android' do
51+
include_examples 'from_xxx', :android, 'fr', 'pt-rBR'
52+
end
53+
54+
describe 'from_google_play' do
55+
include_examples 'from_xxx', :google_play, 'fr-FR', 'pt-BR'
56+
end
57+
58+
# @TODO: from_ios, from_app_store
59+
60+
describe 'subscript [] operator' do
61+
it 'returns an Array<Locale> even if a single one was passed' do
62+
locales = Fastlane::Locales['fr']
63+
expect(locales).to be_instance_of(Array)
64+
expect(locales.count).to equal(1)
65+
expect(locales[0].glotpress).to eq('fr')
66+
end
67+
68+
it 'returns an Array<Locale> if a list of vararg codes was passed' do
69+
locales = Fastlane::Locales['fr', 'pt-br']
70+
expect(locales).to be_instance_of(Array)
71+
expect(locales.count).to equal(2)
72+
expect(locales[0]).to be_instance_of(Fastlane::Locale)
73+
expect(locales[0].glotpress).to eq('fr')
74+
expect(locales[1]).to be_instance_of(Fastlane::Locale)
75+
expect(locales[1].glotpress).to eq('pt-br')
76+
end
77+
78+
it 'returns an Array<Locale> if an Array<String> of codes was passed' do
79+
list = %w[fr pt-br]
80+
locales = Fastlane::Locales[list]
81+
expect(locales).to be_instance_of(Array)
82+
expect(locales.count).to equal(2)
83+
expect(locales[0]).to be_instance_of(Fastlane::Locale)
84+
expect(locales[0].glotpress).to eq('fr')
85+
expect(locales[1]).to be_instance_of(Fastlane::Locale)
86+
expect(locales[1].glotpress).to eq('pt-br')
87+
end
88+
end
89+
90+
it 'returns exactly 16 Mag16 locales' do
91+
expect(Fastlane::Locales.mag16.count).to eq(16)
92+
end
93+
94+
it 'is easy to do Locale subset intersections' do
95+
mag16_except_pt = Fastlane::Locales.mag16 - Fastlane::Locales['pt-br']
96+
expect(mag16_except_pt.count).to equal(15)
97+
expect(mag16_except_pt.find { |l| l.glotpress == 'pt-br' }).to be_nil
98+
expect(mag16_except_pt.find { |l| l.glotpress == 'fr' }).to_not be_nil
99+
end
100+
101+
it 'can convert a Locale to a hash' do
102+
h = Fastlane::Locale['fr'].to_h
103+
expect(h).to eq({ glotpress: 'fr', android: 'fr', google_play: 'fr-FR', ios: nil, app_store: nil })
104+
end
105+
end

0 commit comments

Comments
 (0)