worldwide 0.11.0 → 0.12.0
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 +4 -4
- data/CHANGELOG.md +14 -2
- data/Gemfile +1 -0
- data/Gemfile.lock +14 -4
- data/README.md +2 -0
- data/db/data/regions/BE.yml +8 -0
- data/db/data/regions/BR.yml +15 -0
- data/db/data/regions/CH.yml +1 -0
- data/db/data/regions/CL/en.yml +25 -0
- data/db/data/regions/CL.yml +13 -0
- data/db/data/regions/CO.yml +8 -0
- data/db/data/regions/ES.yml +8 -0
- data/db/data/regions/ID.yml +9 -0
- data/db/data/regions/IN.yml +2 -2
- data/db/data/regions/MX.yml +13 -0
- data/db/data/regions/NF.yml +1 -1
- data/db/data/regions/NL.yml +8 -0
- data/db/data/regions/PH.yml +9 -0
- data/db/data/regions/TR/en.yml +25 -0
- data/db/data/regions/TR.yml +8 -0
- data/db/data/regions/TW/en.yml +25 -0
- data/db/data/regions/TW.yml +2 -0
- data/db/data/regions/VN/en.yml +25 -0
- data/db/data/regions/VN.yml +9 -0
- data/db/data/regions/_default/en.yml +56 -1
- data/docs/address_format_strings.md +111 -0
- data/docs/images/edit.png +0 -0
- data/docs/images/show.png +0 -0
- data/lib/worldwide/field.rb +3 -0
- data/lib/worldwide/region.rb +14 -3
- data/lib/worldwide/regions_loader.rb +2 -0
- data/lib/worldwide/version.rb +1 -1
- data/lib/worldwide/zip.rb +1 -1
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1aced99de70bc88aa872fd56bf0aea847c81bcf3b12987e031ce39a51e9dbdd0
|
|
4
|
+
data.tar.gz: de70d75e3374938d1611ef3c0541ce7d880d4dbf5ad3b45fb4dddeeb6ef75382
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e77fdb3041498e31ab8cec400ca4be14e531939e9f99fa800aae011d1a7be0e076fc6b4e65ea4ca877b98a051dc6e88994b71d27582cb083fb8c71ac0645e65f
|
|
7
|
+
data.tar.gz: bb7c2b6963ab30589320b70db8af56942ce454d01cbf6dc7f13c7da74c86964513de2711a31bd9fa07457e96fcb810f482d89e31a214acf5454f47ec21517dfc
|
data/CHANGELOG.md
CHANGED
|
@@ -27,10 +27,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
27
27
|
|
|
28
28
|
## [Unreleased]
|
|
29
29
|
|
|
30
|
-
- Nil.
|
|
31
|
-
|
|
32
30
|
---
|
|
33
31
|
|
|
32
|
+
## [0.12.0] - 2024-05-15
|
|
33
|
+
|
|
34
|
+
- Add pry-byebug gem [#149](https://github.com/Shopify/worldwide/pull/149)
|
|
35
|
+
- Introduce additional_address_fields to Region class and define them for BE, BR, CL, CO, ES, ID, MX, NL, PH, TR, VN [#148](https://github.com/Shopify/worldwide/pull/148)
|
|
36
|
+
- Define format_extended attribute on 12 country regions [#150](https://github.com/Shopify/worldwide/pull/150)
|
|
37
|
+
|
|
38
|
+
## [0.11.1] - 2024-05-13
|
|
39
|
+
|
|
40
|
+
- Handle blank strings for Region, country_code parameter in Zip normalization
|
|
41
|
+
[#138](https://github.com/Shopify/worldwide/pull/138)
|
|
42
|
+
- Reposition zip in NF show: format to come after country, not before city. [#142](https://github.com/Shopify/worldwide/pull/142)
|
|
43
|
+
- Allow building number on address2 for CH [#143](https://github.com/Shopify/worldwide/pull/143)
|
|
44
|
+
- Attempt to fix stringio incompatibility with Ruby 3.3 (deployment error) [#145](https://github.com/Shopify/worldwide/pull/145)
|
|
45
|
+
|
|
34
46
|
## [0.11.0] - 2024-04-11
|
|
35
47
|
|
|
36
48
|
- Add country_prefix to phone [#133](https://github.com/Shopify/worldwide/pull/133)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -13,7 +13,7 @@ GIT
|
|
|
13
13
|
PATH
|
|
14
14
|
remote: .
|
|
15
15
|
specs:
|
|
16
|
-
worldwide (0.
|
|
16
|
+
worldwide (0.12.0)
|
|
17
17
|
activesupport (~> 7.0)
|
|
18
18
|
i18n
|
|
19
19
|
phonelib (~> 0.8)
|
|
@@ -36,6 +36,8 @@ GEM
|
|
|
36
36
|
base64 (0.1.1)
|
|
37
37
|
bigdecimal (3.1.4)
|
|
38
38
|
builder (3.2.4)
|
|
39
|
+
byebug (11.1.3)
|
|
40
|
+
coderay (1.1.3)
|
|
39
41
|
concurrent-ruby (1.2.2)
|
|
40
42
|
connection_pool (2.4.1)
|
|
41
43
|
drb (2.1.1)
|
|
@@ -44,6 +46,7 @@ GEM
|
|
|
44
46
|
concurrent-ruby (~> 1.0)
|
|
45
47
|
json (2.7.1)
|
|
46
48
|
language_server-protocol (3.17.0.3)
|
|
49
|
+
method_source (1.1.0)
|
|
47
50
|
minitest (5.17.0)
|
|
48
51
|
minitest-focus (1.3.1)
|
|
49
52
|
minitest (>= 4, < 6)
|
|
@@ -55,9 +58,9 @@ GEM
|
|
|
55
58
|
mocha (2.0.2)
|
|
56
59
|
ruby2_keywords (>= 0.0.5)
|
|
57
60
|
mutex_m (0.1.2)
|
|
58
|
-
nokogiri (1.16.
|
|
61
|
+
nokogiri (1.16.5-arm64-darwin)
|
|
59
62
|
racc (~> 1.4)
|
|
60
|
-
nokogiri (1.16.
|
|
63
|
+
nokogiri (1.16.5-x86_64-linux)
|
|
61
64
|
racc (~> 1.4)
|
|
62
65
|
parallel (1.24.0)
|
|
63
66
|
parser (3.3.0.5)
|
|
@@ -65,6 +68,12 @@ GEM
|
|
|
65
68
|
racc
|
|
66
69
|
phonelib (0.8.7)
|
|
67
70
|
prettier_print (1.2.0)
|
|
71
|
+
pry (0.14.2)
|
|
72
|
+
coderay (~> 1.1)
|
|
73
|
+
method_source (~> 1.0)
|
|
74
|
+
pry-byebug (3.10.1)
|
|
75
|
+
byebug (~> 11.0)
|
|
76
|
+
pry (>= 0.13, < 0.15)
|
|
68
77
|
psych (5.1.0)
|
|
69
78
|
stringio
|
|
70
79
|
racc (1.7.3)
|
|
@@ -104,7 +113,7 @@ GEM
|
|
|
104
113
|
ruby2_keywords (0.0.5)
|
|
105
114
|
rubyzip (2.3.2)
|
|
106
115
|
sorbet-runtime (0.5.10648)
|
|
107
|
-
stringio (3.0
|
|
116
|
+
stringio (3.1.0)
|
|
108
117
|
syntax_tree (5.3.0)
|
|
109
118
|
prettier_print (>= 1.2.0)
|
|
110
119
|
thor (1.2.1)
|
|
@@ -122,6 +131,7 @@ DEPENDENCIES
|
|
|
122
131
|
minitest-focus
|
|
123
132
|
minitest-reporters
|
|
124
133
|
mocha
|
|
134
|
+
pry-byebug
|
|
125
135
|
rake (~> 13.0)
|
|
126
136
|
rake-compiler
|
|
127
137
|
rubocop
|
data/README.md
CHANGED
data/db/data/regions/BE.yml
CHANGED
|
@@ -24,5 +24,13 @@ languages:
|
|
|
24
24
|
format:
|
|
25
25
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}_{phone}"
|
|
26
26
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{country}_{phone}"
|
|
27
|
+
format_extended:
|
|
28
|
+
edit: "{country}_{firstName}{lastName}_{company}_{streetName}{streetNumber}_{address2}_{zip}{city}_{phone}"
|
|
29
|
+
additional_address_fields:
|
|
30
|
+
address1:
|
|
31
|
+
- key: streetName
|
|
32
|
+
required: true
|
|
33
|
+
- key: streetNumber
|
|
34
|
+
required: false
|
|
27
35
|
emoji: "\U0001F1E7\U0001F1EA"
|
|
28
36
|
timezone: Europe/Brussels
|
data/db/data/regions/BR.yml
CHANGED
|
@@ -19,6 +19,21 @@ languages:
|
|
|
19
19
|
format:
|
|
20
20
|
edit: "{country}_{firstName}{lastName}_{company}_{zip}_{address1}_{address2}_{city}{province}_{phone}"
|
|
21
21
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city} {province}_{country}_{phone}"
|
|
22
|
+
format_extended:
|
|
23
|
+
edit: "{country}_{firstName}{lastName}_{company}_{zip}_{streetName}{streetNumber}_{address2}{neighborhood}_{city}{province}_{phone}"
|
|
24
|
+
additional_address_fields:
|
|
25
|
+
address1:
|
|
26
|
+
- key: streetName
|
|
27
|
+
required: true
|
|
28
|
+
- key: streetNumber
|
|
29
|
+
decorator: ","
|
|
30
|
+
required: true
|
|
31
|
+
address2:
|
|
32
|
+
- key: address2
|
|
33
|
+
required: false
|
|
34
|
+
- key: neighborhood
|
|
35
|
+
decorator: ","
|
|
36
|
+
required: false
|
|
22
37
|
emoji: "\U0001F1E7\U0001F1F7"
|
|
23
38
|
localized_data:
|
|
24
39
|
- name: tax_credential_br
|
data/db/data/regions/CH.yml
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
worldwide:
|
|
4
|
+
CL:
|
|
5
|
+
addresses:
|
|
6
|
+
neighborhood:
|
|
7
|
+
label:
|
|
8
|
+
default: Commune
|
|
9
|
+
optional: Commune (optional)
|
|
10
|
+
errors:
|
|
11
|
+
blank: Enter a commune
|
|
12
|
+
too_long: Commune is too long (maximum is 255 characters)
|
|
13
|
+
contains_emojis: Commune cannot contain emojis
|
|
14
|
+
contains_mathematical_symbols: Commune cannot contain mathematical symbols
|
|
15
|
+
contains_restricted_characters: Commune can only contain letters, numbers,
|
|
16
|
+
local characters, and special characters
|
|
17
|
+
contains_too_many_words: Commune cannot have more than %{word_count}
|
|
18
|
+
words
|
|
19
|
+
contains_html_tags: Commune cannot contain HTML tags.
|
|
20
|
+
contains_url: Commune cannot contain URLs.
|
|
21
|
+
unknown_for_city: Enter a valid commune name for %{city}.
|
|
22
|
+
unknown_for_zip: Enter a valid commune name for %{zip}.
|
|
23
|
+
unknown_for_address: Commune may be incorrect.
|
|
24
|
+
warnings:
|
|
25
|
+
contains_too_many_words: Commune is recommended to have less than %{word_count} words
|
data/db/data/regions/CL.yml
CHANGED
|
@@ -14,6 +14,19 @@ week_start_day: sunday
|
|
|
14
14
|
format:
|
|
15
15
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}_{province}_{phone}"
|
|
16
16
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{province}_{country}_{phone}"
|
|
17
|
+
format_extended:
|
|
18
|
+
edit: "{country}_{firstName}{lastName}_{company}_{streetName}{streetNumber}_{address2}{neighborhood}_{zip}{city}_{province}_{phone}"
|
|
19
|
+
additional_address_fields:
|
|
20
|
+
address1:
|
|
21
|
+
- key: streetName
|
|
22
|
+
required: true
|
|
23
|
+
- key: streetNumber
|
|
24
|
+
required: false
|
|
25
|
+
address2:
|
|
26
|
+
- key: address2
|
|
27
|
+
required: false
|
|
28
|
+
- key: neighborhood
|
|
29
|
+
required: false
|
|
17
30
|
emoji: "\U0001F1E8\U0001F1F1"
|
|
18
31
|
languages:
|
|
19
32
|
- es
|
data/db/data/regions/CO.yml
CHANGED
|
@@ -15,6 +15,14 @@ week_start_day: sunday
|
|
|
15
15
|
format:
|
|
16
16
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{city}{province}{zip}_{phone}"
|
|
17
17
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city} {province}_{country}_{phone}"
|
|
18
|
+
format_extended:
|
|
19
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}{neighborhood}_{city}{province}{zip}_{phone}"
|
|
20
|
+
additional_address_fields:
|
|
21
|
+
address2:
|
|
22
|
+
- key: address2
|
|
23
|
+
required: false
|
|
24
|
+
- key: neighborhood
|
|
25
|
+
required: false
|
|
18
26
|
emoji: "\U0001F1E8\U0001F1F4"
|
|
19
27
|
languages:
|
|
20
28
|
- es
|
data/db/data/regions/ES.yml
CHANGED
|
@@ -21,6 +21,14 @@ languages:
|
|
|
21
21
|
format:
|
|
22
22
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}{province}_{phone}"
|
|
23
23
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{province}_{country}_{phone}"
|
|
24
|
+
format_extended:
|
|
25
|
+
edit: "{country}_{firstName}{lastName}_{company}_{streetName}{streetNumber}_{address2}_{zip}{city}{province}_{phone}"
|
|
26
|
+
additional_address_fields:
|
|
27
|
+
address1:
|
|
28
|
+
- key: streetName
|
|
29
|
+
required: true
|
|
30
|
+
- key: streetNumber
|
|
31
|
+
required: false
|
|
24
32
|
emoji: "\U0001F1EA\U0001F1F8"
|
|
25
33
|
zones:
|
|
26
34
|
- name: A Coruña
|
data/db/data/regions/ID.yml
CHANGED
|
@@ -17,6 +17,15 @@ languages:
|
|
|
17
17
|
format:
|
|
18
18
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{city}_{province}{zip}_{phone}"
|
|
19
19
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{city}_{province} {zip}_{country}_{phone}"
|
|
20
|
+
format_extended:
|
|
21
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}{neighborhood}_{city}_{province}{zip}_{phone}"
|
|
22
|
+
additional_address_fields:
|
|
23
|
+
address2:
|
|
24
|
+
- key: address2
|
|
25
|
+
required: false
|
|
26
|
+
- key: neighborhood
|
|
27
|
+
decorator: ","
|
|
28
|
+
required: false
|
|
20
29
|
emoji: "\U0001F1EE\U0001F1E9"
|
|
21
30
|
zones:
|
|
22
31
|
- name: Aceh
|
data/db/data/regions/IN.yml
CHANGED
|
@@ -141,8 +141,8 @@ zones:
|
|
|
141
141
|
zip_prefixes:
|
|
142
142
|
- '49'
|
|
143
143
|
# Dadra and Nagar Haveli was merged with Daman and Diu in 2020, and uses the new ISO code DH for the combined zone.
|
|
144
|
-
# But, we need to upgrade our version of CLDR before
|
|
145
|
-
# TODO: import new CLDR in
|
|
144
|
+
# But, we need to upgrade our version of CLDR before worldwide will recognize DH.
|
|
145
|
+
# TODO: import new CLDR in worldwide, merge DD and DN into new zone DH
|
|
146
146
|
- name: Dadra and Nagar Haveli
|
|
147
147
|
code: DN
|
|
148
148
|
tax: 0.18
|
data/db/data/regions/MX.yml
CHANGED
|
@@ -15,6 +15,19 @@ week_start_day: sunday
|
|
|
15
15
|
format:
|
|
16
16
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}{province}_{phone}"
|
|
17
17
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city} {province}_{country}_{phone}"
|
|
18
|
+
format_extended:
|
|
19
|
+
edit: "{country}_{firstName}{lastName}_{company}_{streetName}{streetNumber}_{address2}{neighborhood}_{zip}{city}{province}_{phone}"
|
|
20
|
+
additional_address_fields:
|
|
21
|
+
address1:
|
|
22
|
+
- key: streetName
|
|
23
|
+
required: true
|
|
24
|
+
- key: streetNumber
|
|
25
|
+
required: false
|
|
26
|
+
address2:
|
|
27
|
+
- key: address2
|
|
28
|
+
required: false
|
|
29
|
+
- key: neighborhood
|
|
30
|
+
required: true
|
|
18
31
|
emoji: "\U0001F1F2\U0001F1FD"
|
|
19
32
|
languages:
|
|
20
33
|
- es
|
data/db/data/regions/NF.yml
CHANGED
|
@@ -12,7 +12,7 @@ phone_number_prefix: 672
|
|
|
12
12
|
zip_autofill_enabled: true
|
|
13
13
|
format:
|
|
14
14
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{city}_{phone}"
|
|
15
|
-
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{
|
|
15
|
+
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{city}_{country} {zip}_{phone}"
|
|
16
16
|
emoji: 🇳🇫
|
|
17
17
|
languages:
|
|
18
18
|
- en
|
data/db/data/regions/NL.yml
CHANGED
|
@@ -23,5 +23,13 @@ languages:
|
|
|
23
23
|
format:
|
|
24
24
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}_{phone}"
|
|
25
25
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{country}_{phone}"
|
|
26
|
+
format_extended:
|
|
27
|
+
edit: "{country}_{firstName}{lastName}_{company}_{streetName}{streetNumber}_{address2}_{zip}{city}_{phone}"
|
|
28
|
+
additional_address_fields:
|
|
29
|
+
address1:
|
|
30
|
+
- key: streetName
|
|
31
|
+
required: true
|
|
32
|
+
- key: streetNumber
|
|
33
|
+
required: true
|
|
26
34
|
emoji: "\U0001F1F3\U0001F1F1"
|
|
27
35
|
timezone: Europe/Amsterdam
|
data/db/data/regions/PH.yml
CHANGED
|
@@ -15,6 +15,15 @@ week_start_day: sunday
|
|
|
15
15
|
format:
|
|
16
16
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}_{province}_{phone}"
|
|
17
17
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{province}_{country}_{phone}"
|
|
18
|
+
format_extended:
|
|
19
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}{neighborhood}_{zip}{city}_{province}_{phone}"
|
|
20
|
+
additional_address_fields:
|
|
21
|
+
address2:
|
|
22
|
+
- key: address2
|
|
23
|
+
required: false
|
|
24
|
+
- key: neighborhood
|
|
25
|
+
decorator: "Barangay"
|
|
26
|
+
required: true
|
|
18
27
|
emoji: "\U0001F1F5\U0001F1ED"
|
|
19
28
|
languages:
|
|
20
29
|
- en
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
worldwide:
|
|
4
|
+
TR:
|
|
5
|
+
addresses:
|
|
6
|
+
neighborhood:
|
|
7
|
+
label:
|
|
8
|
+
default: District
|
|
9
|
+
optional: District (optional)
|
|
10
|
+
errors:
|
|
11
|
+
blank: Enter a district
|
|
12
|
+
too_long: District is too long (maximum is 255 characters)
|
|
13
|
+
contains_emojis: District cannot contain emojis
|
|
14
|
+
contains_mathematical_symbols: District cannot contain mathematical symbols
|
|
15
|
+
contains_restricted_characters: District can only contain letters, numbers,
|
|
16
|
+
local characters, and special characters
|
|
17
|
+
contains_too_many_words: District cannot have more than %{word_count}
|
|
18
|
+
words
|
|
19
|
+
contains_html_tags: District cannot contain HTML tags.
|
|
20
|
+
contains_url: District cannot contain URLs.
|
|
21
|
+
unknown_for_city: Enter a valid district name for %{city}.
|
|
22
|
+
unknown_for_zip: Enter a valid district name for %{zip}.
|
|
23
|
+
unknown_for_address: District may be incorrect.
|
|
24
|
+
warnings:
|
|
25
|
+
contains_too_many_words: District is recommended to have less than %{word_count} words
|
data/db/data/regions/TR.yml
CHANGED
|
@@ -16,6 +16,14 @@ week_start_day: monday
|
|
|
16
16
|
format:
|
|
17
17
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{zip}{city}_{phone}"
|
|
18
18
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{country}_{phone}"
|
|
19
|
+
format_extended:
|
|
20
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}{neighborhood}_{zip}{city}_{phone}"
|
|
21
|
+
additional_address_fields:
|
|
22
|
+
address2:
|
|
23
|
+
- key: address2
|
|
24
|
+
required: false
|
|
25
|
+
- key: neighborhood
|
|
26
|
+
required: false
|
|
19
27
|
emoji: "\U0001F1F9\U0001F1F7"
|
|
20
28
|
languages:
|
|
21
29
|
- tr
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
worldwide:
|
|
4
|
+
TW:
|
|
5
|
+
addresses:
|
|
6
|
+
neighborhood:
|
|
7
|
+
label:
|
|
8
|
+
default: District
|
|
9
|
+
optional: District (optional)
|
|
10
|
+
errors:
|
|
11
|
+
blank: Enter a district
|
|
12
|
+
too_long: District is too long (maximum is 255 characters)
|
|
13
|
+
contains_emojis: District cannot contain emojis
|
|
14
|
+
contains_mathematical_symbols: District cannot contain mathematical symbols
|
|
15
|
+
contains_restricted_characters: District can only contain letters, numbers,
|
|
16
|
+
local characters, and special characters
|
|
17
|
+
contains_too_many_words: District cannot have more than %{word_count}
|
|
18
|
+
words
|
|
19
|
+
contains_html_tags: District cannot contain HTML tags.
|
|
20
|
+
contains_url: District cannot contain URLs.
|
|
21
|
+
unknown_for_city: Enter a valid district name for %{city}.
|
|
22
|
+
unknown_for_zip: Enter a valid district name for %{zip}.
|
|
23
|
+
unknown_for_address: District may be incorrect.
|
|
24
|
+
warnings:
|
|
25
|
+
contains_too_many_words: District is recommended to have less than %{word_count} words
|
data/db/data/regions/TW.yml
CHANGED
|
@@ -17,6 +17,8 @@ week_start_day: sunday
|
|
|
17
17
|
format:
|
|
18
18
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{city}{zip}_{phone}"
|
|
19
19
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{zip} {city}_{country}_{phone}"
|
|
20
|
+
format_extended:
|
|
21
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{neighborhood}{city}{zip}_{phone}"
|
|
20
22
|
emoji: "\U0001F1F9\U0001F1FC"
|
|
21
23
|
languages:
|
|
22
24
|
- "zh-TW"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
worldwide:
|
|
4
|
+
VN:
|
|
5
|
+
addresses:
|
|
6
|
+
neighborhood:
|
|
7
|
+
label:
|
|
8
|
+
default: Ward
|
|
9
|
+
optional: Ward (optional)
|
|
10
|
+
errors:
|
|
11
|
+
blank: Enter a ward
|
|
12
|
+
too_long: Ward is too long (maximum is 255 characters)
|
|
13
|
+
contains_emojis: Ward cannot contain emojis
|
|
14
|
+
contains_mathematical_symbols: Ward cannot contain mathematical symbols
|
|
15
|
+
contains_restricted_characters: Ward can only contain letters, numbers,
|
|
16
|
+
local characters, and special characters
|
|
17
|
+
contains_too_many_words: Ward cannot have more than %{word_count}
|
|
18
|
+
words
|
|
19
|
+
contains_html_tags: Ward cannot contain HTML tags.
|
|
20
|
+
contains_url: Ward cannot contain URLs.
|
|
21
|
+
unknown_for_city: Enter a valid ward name for %{city}.
|
|
22
|
+
unknown_for_zip: Enter a valid ward name for %{zip}.
|
|
23
|
+
unknown_for_address: Ward may be incorrect.
|
|
24
|
+
warnings:
|
|
25
|
+
contains_too_many_words: Ward is recommended to have less than %{word_count} words
|
data/db/data/regions/VN.yml
CHANGED
|
@@ -13,6 +13,15 @@ week_start_day: monday
|
|
|
13
13
|
format:
|
|
14
14
|
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{city}{zip}_{phone}"
|
|
15
15
|
show: "{firstName} {lastName}_{company}_{address1}_{address2}_{city} {zip}_{country}_{phone}"
|
|
16
|
+
format_extended:
|
|
17
|
+
edit: "{country}_{firstName}{lastName}_{company}_{address1}_{address2}_{neighborhood}_{city}{zip}_{phone}"
|
|
18
|
+
additional_address_fields:
|
|
19
|
+
address2:
|
|
20
|
+
- key: address2
|
|
21
|
+
required: false
|
|
22
|
+
- key: neighborhood
|
|
23
|
+
decorator: ", Quận"
|
|
24
|
+
required: false
|
|
16
25
|
emoji: "\U0001F1FB\U0001F1F3"
|
|
17
26
|
languages:
|
|
18
27
|
- fr
|
|
@@ -63,6 +63,42 @@ en:
|
|
|
63
63
|
unknown_for_address: Address line 1 may be incorrect.
|
|
64
64
|
warnings:
|
|
65
65
|
contains_too_many_words: Address line 1 is recommended to have less than %{word_count} words
|
|
66
|
+
street_name:
|
|
67
|
+
label:
|
|
68
|
+
default: Street
|
|
69
|
+
optional: Street (optional)
|
|
70
|
+
errors:
|
|
71
|
+
blank: Enter a street name
|
|
72
|
+
too_long: Street is too long (maximum is 255 characters)
|
|
73
|
+
contains_emojis: Street cannot contain emojis
|
|
74
|
+
contains_mathematical_symbols: Street cannot contain mathematical symbols
|
|
75
|
+
contains_restricted_characters: Street can only contain letters, numbers,
|
|
76
|
+
local characters, and special characters
|
|
77
|
+
contains_too_many_words: Street cannot have more than %{word_count} words
|
|
78
|
+
contains_html_tags: Street cannot contain HTML tags.
|
|
79
|
+
contains_url: Street cannot contain URLs.
|
|
80
|
+
street_unknown_for_zip: Enter a valid street name for %{zip}.
|
|
81
|
+
unknown_for_address: Street may be incorrect.
|
|
82
|
+
warnings:
|
|
83
|
+
contains_too_many_words: Street is recommended to have less than %{word_count} words
|
|
84
|
+
street_number:
|
|
85
|
+
label:
|
|
86
|
+
default: Building number
|
|
87
|
+
optional: Building number (optional)
|
|
88
|
+
errors:
|
|
89
|
+
blank: Enter a building number
|
|
90
|
+
too_long: Building number is too long (maximum is 255 characters)
|
|
91
|
+
contains_emojis: Building number cannot contain emojis
|
|
92
|
+
contains_mathematical_symbols: Building number cannot contain mathematical symbols
|
|
93
|
+
contains_restricted_characters: Building number can only contain letters,
|
|
94
|
+
numbers, local characters, and special characters
|
|
95
|
+
contains_html_tags: Building number cannot contain HTML tags.
|
|
96
|
+
contains_url: Building number cannot contain URLs.
|
|
97
|
+
building_number_invalid: Building number couldn't be located for %{street},
|
|
98
|
+
%{zip}.
|
|
99
|
+
unknown_for_address: Building number may be incorrect.
|
|
100
|
+
warnings:
|
|
101
|
+
contains_too_many_words: Building number is recommended to have less than %{word_count} words
|
|
66
102
|
address2:
|
|
67
103
|
label:
|
|
68
104
|
default: Apartment, suite, etc.
|
|
@@ -83,9 +119,28 @@ en:
|
|
|
83
119
|
building_number_invalid: Building number couldn't be located for %{street},
|
|
84
120
|
%{zip}.
|
|
85
121
|
unknown_for_address: Address line 2 may be incorrect.
|
|
86
|
-
|
|
87
122
|
warnings:
|
|
88
123
|
contains_too_many_words: Address line 2 is recommended to have less than %{word_count} words
|
|
124
|
+
neighborhood:
|
|
125
|
+
label:
|
|
126
|
+
default: Neighborhood
|
|
127
|
+
optional: Neighborhood (optional)
|
|
128
|
+
errors:
|
|
129
|
+
blank: Enter a neighborhood
|
|
130
|
+
too_long: Neighborhood is too long (maximum is 255 characters)
|
|
131
|
+
contains_emojis: Neighborhood cannot contain emojis
|
|
132
|
+
contains_mathematical_symbols: Neighborhood cannot contain mathematical symbols
|
|
133
|
+
contains_restricted_characters: Neighborhood can only contain letters, numbers,
|
|
134
|
+
local characters, and special characters
|
|
135
|
+
contains_too_many_words: Neighborhood cannot have more than %{word_count}
|
|
136
|
+
words
|
|
137
|
+
contains_html_tags: Neighborhood cannot contain HTML tags.
|
|
138
|
+
contains_url: Neighborhood cannot contain URLs.
|
|
139
|
+
unknown_for_city: Enter a valid neighborhood name for %{city}.
|
|
140
|
+
unknown_for_zip: Enter a valid neighborhood name for %{zip}.
|
|
141
|
+
unknown_for_address: Neighborhood may be incorrect.
|
|
142
|
+
warnings:
|
|
143
|
+
contains_too_many_words: Neighborhood is recommended to have less than %{word_count} words
|
|
89
144
|
city:
|
|
90
145
|
label:
|
|
91
146
|
default: City
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Address format strings
|
|
2
|
+
|
|
3
|
+
## Introduction <!-- omit in toc -->
|
|
4
|
+
|
|
5
|
+
Worldwide defines different address format strings that control the layout of address elements for each country region.
|
|
6
|
+
|
|
7
|
+
Each format string has a different syntax, and is meant for use in different scenarios.
|
|
8
|
+
|
|
9
|
+
This page aims to document these syntaxes, their usage and limitations for those who need to create/edit them.
|
|
10
|
+
|
|
11
|
+
**⚠️ Warning:** You shouldn't parse these format strings yourself, but instead make use of the higher-level libraries that already consume these strings. This document is meant for those creating the higher-level libraries, or for those who need to make changes to these format strings.
|
|
12
|
+
|
|
13
|
+
## Table of Contents <!-- omit in toc -->
|
|
14
|
+
|
|
15
|
+
- [Address format strings](#address-format-strings)
|
|
16
|
+
- [`edit`](#edit)
|
|
17
|
+
- [English description](#english-description)
|
|
18
|
+
- [Grammar](#grammar)
|
|
19
|
+
- [`show`](#show)
|
|
20
|
+
- [English description](#english-description-1)
|
|
21
|
+
- [Grammar](#grammar-1)
|
|
22
|
+
- [Limitations](#limitations)
|
|
23
|
+
- [No escaping of special characters](#no-escaping-of-special-characters)
|
|
24
|
+
- [No space-less formats](#no-space-less-formats)
|
|
25
|
+
- [No free-standing prefix/suffix characters](#no-free-standing-prefixsuffix-characters)
|
|
26
|
+
- [No special concatenation characters](#no-special-concatenation-characters)
|
|
27
|
+
|
|
28
|
+
## `edit`
|
|
29
|
+
|
|
30
|
+
The `edit` address format string controls the layout of elements within an address input form for a country region.
|
|
31
|
+
|
|
32
|
+
e.g., `{company}_{address1}_{address2}_{city}_{country}{province}{zip}_{phone}`
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
**⚠️ Warning:** There's a lot more to creating an address form than just the layout of the fields (error messages, optionality, placeholders, etc.) You probably shouldn't parse `edit` yourself; use a pre-existing, higher-level library instead. This document is meant for those creating the higher-level libraries.
|
|
37
|
+
|
|
38
|
+
### English description
|
|
39
|
+
|
|
40
|
+
1. Fields are specified by wrapping the field name in curly braces: `{field_name}`
|
|
41
|
+
2. Lines of fields are separated by an underscore `_`
|
|
42
|
+
|
|
43
|
+
### Grammar
|
|
44
|
+
|
|
45
|
+
Below is the EBNF ([extended Backus-Naur form](https://en.wikipedia.org/wiki/Extended_Backus-Naur_form)) for `edit` address format strings:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
EditFormatString ::= Lines
|
|
49
|
+
Lines ::= Line (LineSeparator Lines)?
|
|
50
|
+
LineSeparator ::= '_'
|
|
51
|
+
Line ::= Field+
|
|
52
|
+
Field ::= '{' Identifier '}'
|
|
53
|
+
Identifier ::= [a-zA-Z0-9_]+
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## `show`
|
|
57
|
+
|
|
58
|
+
The `show` address format string controls how addresses should be rendered as part of a UI.
|
|
59
|
+
|
|
60
|
+
e.g., `{firstName} {lastName}_{company}_{address1}_{address2}_{city} {province} {zip}_{country}_{phone}`
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
### English description
|
|
65
|
+
|
|
66
|
+
1. Fields are specified by wrapping the field name in curly braces: `{field_name}`
|
|
67
|
+
2. Lines of fields are separated by an underscore `_`
|
|
68
|
+
3. Fields are separated by an space character
|
|
69
|
+
4. Other characters are permitted as prefixes/suffixes of a field
|
|
70
|
+
* Note: the prefix of a field is separated from the suffix of a preceding field by the space character separating the fields
|
|
71
|
+
|
|
72
|
+
### Grammar
|
|
73
|
+
|
|
74
|
+
Below is the EBNF ([extended Backus-Naur form](https://en.wikipedia.org/wiki/Extended_Backus-Naur_form)) for `show` address format strings:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
ShowFormatString ::= Lines
|
|
78
|
+
Lines ::= Line (LineSeparator Lines)?
|
|
79
|
+
LineSeparator ::= '_'
|
|
80
|
+
Line ::= PrefixedSuffixedField (FieldSeparator PrefixedSuffixedField)*
|
|
81
|
+
FieldSeparator ::= ' '
|
|
82
|
+
PrefixedSuffixedField ::= (Prefix? Field Suffix?)
|
|
83
|
+
Field ::= '{' Identifier '}'
|
|
84
|
+
Identifier ::= [a-zA-Z0-9_]+
|
|
85
|
+
Prefix ::= Text
|
|
86
|
+
Suffix ::= Text
|
|
87
|
+
Text ::= [^_{} ]+
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Limitations
|
|
91
|
+
|
|
92
|
+
There are a number of limitations to the `show` syntax that cannot cover the complexity of the world's address formatting.
|
|
93
|
+
|
|
94
|
+
#### No escaping of special characters
|
|
95
|
+
|
|
96
|
+
There is no support for escaping special characters: `{`, `}`, `_`, `<space>`
|
|
97
|
+
|
|
98
|
+
#### No space-less formats
|
|
99
|
+
|
|
100
|
+
Since the fields are space-separated, there is no way to specify a non-separator space character.
|
|
101
|
+
However, many languages (e.g., English) are space-delimited, so libraries using the `show` string end up concatenatiing the fields together using spaces, even if the language doesn't require spaces (e.g., Japanese).
|
|
102
|
+
|
|
103
|
+
#### No free-standing prefix/suffix characters
|
|
104
|
+
|
|
105
|
+
There is no way to specify prefix/suffix characters that will be included regardless of the provided values.
|
|
106
|
+
Any characters besides a field name are either prefixes or suffixes of a field, and therefore if a value for the field is not provided, the associated prefix/suffix characters are also not provided.
|
|
107
|
+
|
|
108
|
+
#### No special concatenation characters
|
|
109
|
+
|
|
110
|
+
There is no way to specify characters other than space characters for concatenating two fields.
|
|
111
|
+
For example, in Brazil, when the `city` and `province` fields are formatted adjacent to one another, instead of a space between the fields, ` - ` should be used. These concatenation characters are neither a prefix nor a suffix of either field, so cannot be specified in this format's syntax.
|
|
Binary file
|
|
Binary file
|
data/lib/worldwide/field.rb
CHANGED
data/lib/worldwide/region.rb
CHANGED
|
@@ -19,6 +19,7 @@ module Worldwide
|
|
|
19
19
|
:example_city,
|
|
20
20
|
:flag,
|
|
21
21
|
:format,
|
|
22
|
+
:format_extended,
|
|
22
23
|
:group,
|
|
23
24
|
:group_name,
|
|
24
25
|
:cldr_code,
|
|
@@ -32,6 +33,7 @@ module Worldwide
|
|
|
32
33
|
:zip_example,
|
|
33
34
|
:zip_regex,
|
|
34
35
|
:zip_requirement,
|
|
36
|
+
:additional_address_fields,
|
|
35
37
|
]
|
|
36
38
|
|
|
37
39
|
# A region may have more than one parent.
|
|
@@ -73,9 +75,15 @@ module Worldwide
|
|
|
73
75
|
# - show: how to arrange the fields when formatting an address for display
|
|
74
76
|
attr_accessor :format
|
|
75
77
|
|
|
78
|
+
# Hash of strings denoting how to format an address in this region, including substitute and/or additional fields.
|
|
79
|
+
# The format is described in https://shopify.engineering/handling-addresses-from-all-around-the-world
|
|
80
|
+
# - edit: the fields to present on an address input form
|
|
81
|
+
# - show: how to arrange the fields when formatting an address for display
|
|
82
|
+
attr_accessor :format_extended
|
|
83
|
+
|
|
76
84
|
# The string that results from appending " Countries" to the adjectival form of the {group_name}
|
|
77
85
|
# @example
|
|
78
|
-
#
|
|
86
|
+
# Worldwide.region(code: "CA").group == "North American Countries"
|
|
79
87
|
attr_accessor :group
|
|
80
88
|
|
|
81
89
|
# The continent that this region is part of.
|
|
@@ -101,13 +109,11 @@ module Worldwide
|
|
|
101
109
|
# The code used by the legacy Shopify ecosystem for this region.
|
|
102
110
|
# E.g., for MX-CMX it will return "DF".
|
|
103
111
|
# This code should _never_ be shown in the user interface.
|
|
104
|
-
# This is the code that was traditionally returned by "country_db".
|
|
105
112
|
attr_reader :legacy_code
|
|
106
113
|
|
|
107
114
|
# The name used by the legacy Shopify ecosystem for this region.
|
|
108
115
|
# E.g., "Sao Tome And[sic] Principe" for "ST".
|
|
109
116
|
# This name should _never_ be shown in the user interface.
|
|
110
|
-
# This name is the name that was traditionally returned by "country_db".
|
|
111
117
|
attr_reader :legacy_name
|
|
112
118
|
|
|
113
119
|
# Other names that may be used to refer to this region.
|
|
@@ -191,6 +197,9 @@ module Worldwide
|
|
|
191
197
|
# If true, then the province is optional for addresses in this region.
|
|
192
198
|
attr_accessor :province_optional
|
|
193
199
|
|
|
200
|
+
# A hash of additional address fields and the rules for concatening them into the standard fields
|
|
201
|
+
attr_accessor :additional_address_fields
|
|
202
|
+
|
|
194
203
|
def initialize(
|
|
195
204
|
alpha_three: nil,
|
|
196
205
|
continent: false,
|
|
@@ -226,11 +235,13 @@ module Worldwide
|
|
|
226
235
|
@tax_rate = tax_rate
|
|
227
236
|
@use_zone_code_as_short_name = use_zone_code_as_short_name
|
|
228
237
|
|
|
238
|
+
@additional_address_fields = {}
|
|
229
239
|
@building_number_required = false
|
|
230
240
|
@building_number_may_be_in_address2 = false
|
|
231
241
|
@currency = nil
|
|
232
242
|
@flag = nil
|
|
233
243
|
@format = {}
|
|
244
|
+
@format_extended = {}
|
|
234
245
|
@group = nil
|
|
235
246
|
@group_name = nil
|
|
236
247
|
@languages = []
|
|
@@ -95,6 +95,7 @@ module Worldwide
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def apply_territory_attributes(region, spec)
|
|
98
|
+
region.additional_address_fields = spec["additional_address_fields"] || {}
|
|
98
99
|
region.building_number_required = spec["building_number_required"] || false
|
|
99
100
|
region.building_number_may_be_in_address2 = spec["building_number_may_be_in_address2"] || false
|
|
100
101
|
currency_code = spec["currency"]
|
|
@@ -102,6 +103,7 @@ module Worldwide
|
|
|
102
103
|
region.currency = Worldwide.currency(code: currency_code) unless currency_code.nil?
|
|
103
104
|
region.flag = spec["emoji"]
|
|
104
105
|
region.format = spec["format"]
|
|
106
|
+
region.format_extended = spec["format_extended"] || {}
|
|
105
107
|
region.group = spec["group"]
|
|
106
108
|
region.group_name = spec["group_name"]
|
|
107
109
|
region.hide_provinces_from_addresses = spec["hide_provinces_from_addresses"] || false
|
data/lib/worldwide/version.rb
CHANGED
data/lib/worldwide/zip.rb
CHANGED
|
@@ -85,7 +85,7 @@ module Worldwide
|
|
|
85
85
|
def normalize(country_code:, zip:, allow_autofill: true, strip_extraneous_characters: false)
|
|
86
86
|
input = zip # preserve the original zip, in case we need to fall back to it
|
|
87
87
|
|
|
88
|
-
return input if
|
|
88
|
+
return input if Util.blank?(country_code)
|
|
89
89
|
|
|
90
90
|
country = Worldwide.region(code: country_code)
|
|
91
91
|
return zip if country.nil? || NORMALIZATION_DISABLED_COUNTRIES.include?(country.iso_code)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: worldwide
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-05-15 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -6552,6 +6552,7 @@ files:
|
|
|
6552
6552
|
- db/data/regions/CI.yml
|
|
6553
6553
|
- db/data/regions/CK.yml
|
|
6554
6554
|
- db/data/regions/CL.yml
|
|
6555
|
+
- db/data/regions/CL/en.yml
|
|
6555
6556
|
- db/data/regions/CM.yml
|
|
6556
6557
|
- db/data/regions/CN.yml
|
|
6557
6558
|
- db/data/regions/CN/bg-BG.yml
|
|
@@ -7947,9 +7948,11 @@ files:
|
|
|
7947
7948
|
- db/data/regions/TN.yml
|
|
7948
7949
|
- db/data/regions/TO.yml
|
|
7949
7950
|
- db/data/regions/TR.yml
|
|
7951
|
+
- db/data/regions/TR/en.yml
|
|
7950
7952
|
- db/data/regions/TT.yml
|
|
7951
7953
|
- db/data/regions/TV.yml
|
|
7952
7954
|
- db/data/regions/TW.yml
|
|
7955
|
+
- db/data/regions/TW/en.yml
|
|
7953
7956
|
- db/data/regions/TZ.yml
|
|
7954
7957
|
- db/data/regions/UA.yml
|
|
7955
7958
|
- db/data/regions/UG.yml
|
|
@@ -8061,6 +8064,7 @@ files:
|
|
|
8061
8064
|
- db/data/regions/VE.yml
|
|
8062
8065
|
- db/data/regions/VG.yml
|
|
8063
8066
|
- db/data/regions/VN.yml
|
|
8067
|
+
- db/data/regions/VN/en.yml
|
|
8064
8068
|
- db/data/regions/VU.yml
|
|
8065
8069
|
- db/data/regions/WF.yml
|
|
8066
8070
|
- db/data/regions/WS.yml
|
|
@@ -8171,6 +8175,9 @@ files:
|
|
|
8171
8175
|
- db/data/regions/_default/zh-TW.yml
|
|
8172
8176
|
- db/data/world.yml
|
|
8173
8177
|
- db/extant_outcodes.yml
|
|
8178
|
+
- docs/address_format_strings.md
|
|
8179
|
+
- docs/images/edit.png
|
|
8180
|
+
- docs/images/show.png
|
|
8174
8181
|
- formats.md
|
|
8175
8182
|
- lib/worldwide.rb
|
|
8176
8183
|
- lib/worldwide/address.rb
|
|
@@ -8233,7 +8240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
8233
8240
|
- !ruby/object:Gem::Version
|
|
8234
8241
|
version: '0'
|
|
8235
8242
|
requirements: []
|
|
8236
|
-
rubygems_version: 3.5.
|
|
8243
|
+
rubygems_version: 3.5.10
|
|
8237
8244
|
signing_key:
|
|
8238
8245
|
specification_version: 4
|
|
8239
8246
|
summary: Internationalization and localization APIs
|