Skip to content

Commit efd8ab9

Browse files
authored
feat: Serbia (#25)
* chore: minify large data files * chore: data requirements markdown * feat: Serbia * Revert "chore: minify large data files" This reverts commit 63c5557. * fix: prettify new data files
1 parent 1677def commit efd8ab9

7 files changed

Lines changed: 354 additions & 0 deletions

File tree

DATA_REQUIREMENTS.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Country Data Requirements
2+
Guide for adding a new country to the static Region Identifier dataset. Each supported country needs three JSON files plus a small code change.
3+
4+
## 1) Country config – `country/ISO3.json`
5+
- Keys:
6+
- `zipCodeFormat`: `"numeric"` or `"alpha"`.
7+
- `zipCodeLength`: integer, required for numeric formats to pad/compare values (USA=5, AUS=4, DEU=5, etc.). Omit for alpha formats (CAN/GBR/NLD).
8+
- Example:
9+
```json
10+
{ "zipCodeFormat": "numeric", "zipCodeLength": 5 }
11+
```
12+
13+
## 2) Region names – `regionNames/ISO3.json`
14+
- Plain object mapping ISO 3166-2 region codes (same codes used in `regions/*.json`) to display names in English.
15+
- Example (`regionNames/AUS.json`):
16+
```json
17+
{
18+
"AU-NSW": "New South Wales",
19+
"AU-VIC": "Victoria"
20+
}
21+
```
22+
23+
## 3) Region mappings – `regions/ISO3.json`
24+
Array of objects that map postal codes to ISO 3166-2 region codes. Formats already used in the repo:
25+
- **Numeric intervals** (inclusive): objects with `low`, `high`, `region`. Values can be numbers or numeric strings if leading zeros matter. Example (`regions/AUS.json`):
26+
```json
27+
{ "region": "AU-NSW", "low": 1000, "high": 1999 }
28+
```
29+
- **Explicit codes**: objects with `low`, `region`, no `high`. Every postal code (or prefix) must be listed. Use strings to preserve zeros. Examples:
30+
- Belgium lists every 4-digit code (`regions/BEL.json`).
31+
- Spain lists 5-digit codes as strings (`regions/ESP.json`).
32+
- Mexico lists 2-digit prefixes (`regions/MEX.json`).
33+
- CAN/GBR use alpha prefixes (e.g., `"low": "X0A"` or `"low": "AB"`).
34+
- **Discrete lists**: objects with `region` and `list` (array of codes). Numeric countries store numbers; matching uses `zipCodeLength` to pad leading zeros before comparison. Examples: USA (`list` of 5-digit numbers), RUS (per-region lists).
35+
36+
Additional format notes:
37+
- Files can mix formats if needed. Order matters because the lookup stops at the first match.
38+
- Intervals and lists are treated as inclusive ranges; for alpha codes only exact equality is used.
39+
- Keep codes as strings when leading zeros are significant.
40+
41+
## 4) Code wiring
42+
- Add the ISO3 code to `availableCountries` in `lib/region.js`; otherwise the static data is ignored and Google is used.
43+
- If the country needs custom postal-code normalization, extend `validateZipCode` (see existing cases for GBR, CAN, NLD, MEX).
44+
45+
## 5) Testing
46+
- Add test cases to `test/tests.js` under `countriesPostalCodes`: include the country ISO/name, a representative zip, expected region code, and set `usingGoogle: false`.
47+
- Run `npm test` to execute Mocha tests.
48+
- Optional: verify pretty-name lookups via `getNameFromCountryAndRegion` using the `regionNames` file.
49+
50+
## 6) Data quality checklist
51+
- Region codes must be valid ISO 3166-2 for the country.
52+
- `regionNames` should be English and cover every region code present in `regions/ISO3.json`.
53+
- Postal-code coverage should include territories and edge prefixes (e.g., US territories, Canadian northern prefixes).
54+
- Prefer ascending ordering of `regions` entries to keep matching predictable.
55+
- Avoid empty mappings; placeholders (e.g., empty lists) will yield null regions and fall back to Google.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Utility module that provides an easy way to identify the region of the country d
2121
- MEX
2222
- NLD
2323
- RUS
24+
- SRB
2425
- SWE
2526
- USA
2627

country/SRB.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"zipCodeFormat": "numeric",
3+
"zipCodeLength": 5
4+
}

lib/region.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const availableCountries = new Set([
1818
'MEX',
1919
'NLD',
2020
'RUS',
21+
'SRB',
2122
'SWE',
2223
'USA',
2324
'AUS',

regionNames/SRB.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"RS-00": "Belgrade",
3+
"RS-01": "North Bačka",
4+
"RS-02": "Central Banat",
5+
"RS-03": "North Banat",
6+
"RS-04": "South Banat",
7+
"RS-05": "West Bačka",
8+
"RS-06": "South Bačka",
9+
"RS-07": "Srem",
10+
"RS-08": "Mačva",
11+
"RS-09": "Kolubara",
12+
"RS-10": "Podunavlje",
13+
"RS-11": "Braničevo",
14+
"RS-12": "Šumadija",
15+
"RS-13": "Pomoravlje",
16+
"RS-14": "Bor",
17+
"RS-15": "Zaječar",
18+
"RS-16": "Zlatibor",
19+
"RS-17": "Moravica",
20+
"RS-18": "Raška",
21+
"RS-19": "Rasina",
22+
"RS-20": "Nišava",
23+
"RS-21": "Toplica",
24+
"RS-22": "Pirot",
25+
"RS-23": "Jablanica",
26+
"RS-24": "Pčinja"
27+
}

regions/SRB.json

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
[
2+
{
3+
"region": "RS-00",
4+
"low": 11000,
5+
"high": 11299
6+
},
7+
{
8+
"region": "RS-00",
9+
"list": [
10+
11306,
11+
11307,
12+
11308,
13+
11309,
14+
11351
15+
]
16+
},
17+
{
18+
"region": "RS-10",
19+
"low": 11300,
20+
"high": 11305
21+
},
22+
{
23+
"region": "RS-10",
24+
"low": 11310,
25+
"high": 11325
26+
},
27+
{
28+
"region": "RS-10",
29+
"low": 11328,
30+
"high": 11329
31+
},
32+
{
33+
"region": "RS-10",
34+
"list": [
35+
11420,
36+
11423,
37+
11425,
38+
11431,
39+
11432
40+
]
41+
},
42+
{
43+
"region": "RS-00",
44+
"low": 11400,
45+
"high": 11419
46+
},
47+
{
48+
"region": "RS-00",
49+
"low": 11426,
50+
"high": 11427
51+
},
52+
{
53+
"region": "RS-00",
54+
"list": [
55+
11430,
56+
11433
57+
]
58+
},
59+
{
60+
"region": "RS-00",
61+
"low": 11450,
62+
"high": 11462
63+
},
64+
{
65+
"region": "RS-00",
66+
"low": 11500,
67+
"high": 11599
68+
},
69+
{
70+
"region": "RS-02",
71+
"list": [
72+
11467
73+
]
74+
},
75+
{
76+
"region": "RS-11",
77+
"low": 12000,
78+
"high": 12399
79+
},
80+
{
81+
"region": "RS-09",
82+
"low": 14000,
83+
"high": 14299
84+
},
85+
{
86+
"region": "RS-08",
87+
"low": 15000,
88+
"high": 15399
89+
},
90+
{
91+
"region": "RS-23",
92+
"low": 16000,
93+
"high": 16299
94+
},
95+
{
96+
"region": "RS-24",
97+
"low": 17500,
98+
"high": 17599
99+
},
100+
{
101+
"region": "RS-15",
102+
"low": 18230,
103+
"high": 18237
104+
},
105+
{
106+
"region": "RS-20",
107+
"low": 18000,
108+
"high": 18229
109+
},
110+
{
111+
"region": "RS-20",
112+
"low": 18238,
113+
"high": 18269
114+
},
115+
{
116+
"region": "RS-22",
117+
"low": 18300,
118+
"high": 18359
119+
},
120+
{
121+
"region": "RS-20",
122+
"low": 18360,
123+
"high": 18369
124+
},
125+
{
126+
"region": "RS-21",
127+
"low": 18400,
128+
"high": 18499
129+
},
130+
{
131+
"region": "RS-15",
132+
"low": 19000,
133+
"high": 19099
134+
},
135+
{
136+
"region": "RS-15",
137+
"low": 19200,
138+
"high": 19209
139+
},
140+
{
141+
"region": "RS-15",
142+
"low": 19214,
143+
"high": 19214
144+
},
145+
{
146+
"region": "RS-15",
147+
"low": 19227,
148+
"high": 19228
149+
},
150+
{
151+
"region": "RS-14",
152+
"low": 19210,
153+
"high": 19229
154+
},
155+
{
156+
"region": "RS-15",
157+
"low": 19233,
158+
"high": 19235
159+
},
160+
{
161+
"region": "RS-14",
162+
"low": 19250,
163+
"high": 19259
164+
},
165+
{
166+
"region": "RS-14",
167+
"low": 19300,
168+
"high": 19335
169+
},
170+
{
171+
"region": "RS-15",
172+
"low": 19340,
173+
"high": 19378
174+
},
175+
{
176+
"region": "RS-06",
177+
"low": 21000,
178+
"high": 21499
179+
},
180+
{
181+
"region": "RS-07",
182+
"low": 22000,
183+
"high": 22499
184+
},
185+
{
186+
"region": "RS-02",
187+
"low": 23100,
188+
"high": 23299
189+
},
190+
{
191+
"region": "RS-03",
192+
"low": 23300,
193+
"high": 23399
194+
},
195+
{
196+
"region": "RS-01",
197+
"low": 24000,
198+
"high": 24399
199+
},
200+
{
201+
"region": "RS-03",
202+
"low": 24400,
203+
"high": 24499
204+
},
205+
{
206+
"region": "RS-05",
207+
"low": 25000,
208+
"high": 25299
209+
},
210+
{
211+
"region": "RS-04",
212+
"low": 26000,
213+
"high": 26399
214+
},
215+
{
216+
"region": "RS-16",
217+
"low": 31000,
218+
"high": 31399
219+
},
220+
{
221+
"region": "RS-17",
222+
"low": 32000,
223+
"high": 32399
224+
},
225+
{
226+
"region": "RS-12",
227+
"low": 34000,
228+
"high": 34399
229+
},
230+
{
231+
"region": "RS-13",
232+
"low": 35000,
233+
"high": 35299
234+
},
235+
{
236+
"region": "RS-18",
237+
"low": 36000,
238+
"high": 36399
239+
},
240+
{
241+
"region": "RS-19",
242+
"low": 37000,
243+
"high": 37299
244+
}
245+
]

test/tests.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,27 @@ const countriesPostalCodes = {
206206
result: 'MX-CMX',
207207
usingGoogle: false,
208208
}],
209+
'Serbia': [{
210+
name: 'SRB',
211+
zip: '11000',
212+
result: 'RS-00',
213+
usingGoogle: false,
214+
}, {
215+
name: 'SRB',
216+
zip: '11320',
217+
result: 'RS-10',
218+
usingGoogle: false,
219+
}, {
220+
name: 'SRB',
221+
zip: '18230',
222+
result: 'RS-15',
223+
usingGoogle: false,
224+
}, {
225+
name: 'SRB',
226+
zip: '24430',
227+
result: 'RS-03',
228+
usingGoogle: false,
229+
}],
209230
'United States': [{
210231
name: 'USA',
211232
zip: '972022239',

0 commit comments

Comments
 (0)