@osimatic/helpers-js 1.5.29 → 1.5.31

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.
package/location.js CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
  const Address = require('ilib/lib/Address');
3
3
  const AddressFmt = require('ilib/lib/AddressFmt');
4
+ const { HTTPClient } = require('./http_client');
4
5
  const { toEl } = require('./util');
5
6
  const isoCountries = require('i18n-iso-countries');
6
7
  isoCountries.registerLocale(require('i18n-iso-countries/langs/en.json'));
@@ -11,16 +12,19 @@ class Country {
11
12
  }
12
13
 
13
14
  static getFlagPath(countryCode) {
14
- return (typeof Country.flagsPath != 'undefined' ? Country.flagsPath : '/')+countryCode.toLowerCase()+'.png';
15
+ return typeof Country.flagsPath !== 'undefined' ? Country.flagsPath + countryCode.toLowerCase() + '.png' : null;
15
16
  }
16
- static getFlagImg(countryCode) {
17
- return '<span><img src="'+Country.getFlagPath(countryCode)+'" alt="" title="'+Country.getCountryName(countryCode)+'" class="flag" /></span>'
17
+ static getFlagImg(countryCode, locale='fr-FR') {
18
+ if (typeof Country.flagsPath !== 'undefined') {
19
+ return '<span><img src="'+Country.getFlagPath(countryCode)+'" alt="" title="'+Country.getCountryName(countryCode, locale)+'" class="flag" /></span>';
20
+ }
21
+ return '<span class="fi fi-' + countryCode.toLowerCase() + '"></span>';
18
22
  }
19
23
  static getFlagEmoji(countryCode) {
20
24
  return [...countryCode.toUpperCase()].map(c => String.fromCodePoint(0x1F1E6 - 65 + c.charCodeAt(0))).join('');
21
25
  }
22
26
 
23
- static fillSelect(select, defaultValue=null, showFlags=false, countriesList=null, addNoneValue=false, noneLabel='- Aucun -', locale='fr-FR') {
27
+ static fillSelect(select, defaultValue=null, showFlags=false, locale='fr-FR', countriesList=null, addNoneValue=false, noneLabel='- Aucun -') {
24
28
  select = toEl(select);
25
29
  if (!select) {
26
30
  return;
@@ -33,14 +37,13 @@ class Country {
33
37
  const entries = countriesList != null
34
38
  ? countriesList.map(code => [code, allCountries[code] || code])
35
39
  : Object.entries(allCountries);
36
- entries.forEach(([countryCode, countryName]) => {
40
+ entries.forEach(([countryCode, countryName]) => {
37
41
  let attrs = '';
38
42
  if (showFlags) {
39
- if (typeof Country.flagsPath !== 'undefined') {
43
+ /*if (typeof Country.flagsPath !== 'undefined') {
40
44
  attrs = ' data-thumbnail="' + Country.getFlagPath(countryCode) + '"';
41
- } else {
42
- attrs = ' data-content="<span class=&quot;fi fi-' + countryCode.toLowerCase() + '&quot;></span> ' + countryName + '"';
43
- }
45
+ } else {*/
46
+ attrs = ' data-content="<span class=&quot;fi fi-' + countryCode.toLowerCase() + '&quot;></span> ' + countryName + '"';
44
47
  }
45
48
  select.insertAdjacentHTML('beforeend', '<option value="' + countryCode + '"' + attrs + '>' + countryName + '</option>');
46
49
  });
@@ -50,24 +53,28 @@ class Country {
50
53
  }
51
54
  }
52
55
 
53
- static fillSelectWithFlags(select, defaultValue=null, countriesList=null, addNoneValue=false, noneLabel='- Aucun -', locale='fr-FR') {
54
- return Country.fillSelect(select, defaultValue, true, countriesList, addNoneValue, noneLabel, locale);
56
+ static fillSelectWithFlags(select, defaultValue=null, locale='fr-FR', countriesList=null, addNoneValue=false, noneLabel='- Aucun -') {
57
+ return Country.fillSelect(select, defaultValue, true, locale, countriesList, addNoneValue, noneLabel);
55
58
  }
56
59
 
57
60
  static getCountryName(countryCode, locale='fr-FR') {
58
- const name = isoCountries.getName(countryCode, locale);
61
+ const baseLang = Locale.getBaseLang(locale);
62
+ Country.getCountries(locale); // ensure locale is registered
63
+ const name = isoCountries.getName(countryCode, baseLang);
59
64
  return name || countryCode;
60
65
  }
61
66
  static getCountries(locale = 'en') {
62
- const names = isoCountries.getNames(locale);
67
+ const baseLang = Locale.getBaseLang(locale);
68
+ let names = isoCountries.getNames(baseLang);
63
69
  if (!names || Object.keys(names).length === 0) {
64
70
  try {
65
- isoCountries.registerLocale(require('i18n-iso-countries/langs/' + locale + '.json'));
71
+ isoCountries.registerLocale(require('i18n-iso-countries/langs/' + baseLang + '.json'));
72
+ names = isoCountries.getNames(baseLang);
66
73
  } catch (e) {
67
74
  // locale not available
68
75
  }
69
76
  }
70
- return isoCountries.getNames(locale);
77
+ return names || {};
71
78
  }
72
79
 
73
80
  static getContinents() {
@@ -529,4 +536,35 @@ class GeographicCoordinates {
529
536
 
530
537
  }
531
538
 
532
- module.exports = { Country, PostalAddress, GeographicCoordinates, Polygon };
539
+ class Locale {
540
+ static getBaseLang(locale) {
541
+ return Locale.normalize(locale).split('-')[0];
542
+ }
543
+
544
+ static getRegion(locale) {
545
+ const parts = Locale.normalize(locale).split('-');
546
+ return parts.length > 1 ? parts[1] : null;
547
+ }
548
+
549
+ static normalize(locale) {
550
+ // Accept both 'fr_FR' and 'fr-fr', output 'fr-FR'
551
+ const parts = locale.replace('_', '-').split('-');
552
+ const lang = parts[0].toLowerCase();
553
+ const region = parts[1] ? parts[1].toUpperCase() : null;
554
+ return region ? lang + '-' + region : lang;
555
+ }
556
+
557
+ static isValid(locale) {
558
+ return /^[a-zA-Z]{2,3}(-[a-zA-Z]{2,3})?$/.test(locale);
559
+ }
560
+
561
+ static toPOSIX(locale) {
562
+ return Locale.normalize(locale).replace('-', '_');
563
+ }
564
+
565
+ static update(locale) {
566
+ HTTPClient.setHeader('Accept-Language', Locale.normalize(locale));
567
+ }
568
+ }
569
+
570
+ module.exports = { Country, Locale, PostalAddress, GeographicCoordinates, Polygon };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osimatic/helpers-js",
3
- "version": "1.5.29",
3
+ "version": "1.5.31",
4
4
  "main": "main.js",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -1,4 +1,4 @@
1
- const { Country, PostalAddress, GeographicCoordinates, Polygon } = require('../location');
1
+ const { Country, Locale, PostalAddress, GeographicCoordinates, Polygon } = require('../location');
2
2
 
3
3
  describe('Country', () => {
4
4
  describe('getCountries', () => {
@@ -19,12 +19,23 @@ describe('Country', () => {
19
19
  });
20
20
 
21
21
  describe('getCountryName', () => {
22
- test('should return country name for valid code', () => {
22
+ test('should return country name for valid code (default fr-FR locale)', () => {
23
23
  expect(Country.getCountryName('FR')).toBe('France');
24
- expect(Country.getCountryName('US')).toBe('United States of America');
24
+ expect(Country.getCountryName('US')).toBe("États-Unis d'Amérique");
25
25
  expect(Country.getCountryName('CA')).toBe('Canada');
26
26
  });
27
27
 
28
+ test('should return country name in specified locale', () => {
29
+ expect(Country.getCountryName('US', 'en')).toBe('United States of America');
30
+ expect(Country.getCountryName('DE', 'de')).toBeTruthy();
31
+ });
32
+
33
+ test('should handle full locale with region (fr-FR, en-US)', () => {
34
+ expect(Country.getCountryName('FR', 'fr-FR')).toBe('France');
35
+ expect(Country.getCountryName('US', 'fr-FR')).toBe("États-Unis d'Amérique");
36
+ expect(Country.getCountryName('US', 'en-US')).toBe('United States of America');
37
+ });
38
+
28
39
  test('should return the code itself if country not found', () => {
29
40
  expect(Country.getCountryName('XX')).toBe('XX');
30
41
  expect(Country.getCountryName('ZZZ')).toBe('ZZZ');
@@ -42,50 +53,35 @@ describe('Country', () => {
42
53
  });
43
54
 
44
55
  describe('getFlagPath', () => {
45
- test('should return correct flag path', () => {
46
- const path = Country.getFlagPath('FR');
47
- expect(path).toContain('fr.png');
48
- });
49
-
50
- test('should lowercase the country code', () => {
51
- const path = Country.getFlagPath('US');
52
- expect(path).toContain('us.png');
56
+ test('should return null when no flagsPath set', () => {
57
+ Country.flagsPath = undefined;
58
+ expect(Country.getFlagPath('FR')).toBeNull();
53
59
  });
54
60
 
55
- test('should use custom flags path when set', () => {
61
+ test('should return image path when flagsPath set', () => {
56
62
  Country.setFlagsPath('/custom/flags/');
57
- const path = Country.getFlagPath('FR');
58
- expect(path).toBe('/custom/flags/fr.png');
59
-
60
- // Reset for other tests
63
+ expect(Country.getFlagPath('FR')).toBe('/custom/flags/fr.png');
64
+ expect(Country.getFlagPath('US')).toBe('/custom/flags/us.png');
61
65
  Country.flagsPath = undefined;
62
66
  });
67
+ });
63
68
 
64
- test('should use default path when not set', () => {
69
+ describe('getFlagImg', () => {
70
+ test('should return flag-icons span when no flagsPath set', () => {
65
71
  Country.flagsPath = undefined;
66
- const path = Country.getFlagPath('DE');
67
- expect(path).toBe('/de.png');
72
+ const html = Country.getFlagImg('FR');
73
+ expect(html).toContain('<span');
74
+ expect(html).toContain('fi fi-fr');
68
75
  });
69
- });
70
76
 
71
- describe('getFlagImg', () => {
72
- test('should return HTML img tag', () => {
77
+ test('should return img tag when flagsPath set', () => {
78
+ Country.setFlagsPath('/flags/');
73
79
  const html = Country.getFlagImg('FR');
74
80
  expect(html).toContain('<img');
75
- expect(html).toContain('src=');
76
81
  expect(html).toContain('fr.png');
77
82
  expect(html).toContain('class="flag"');
78
- });
79
-
80
- test('should include country name in title', () => {
81
- const html = Country.getFlagImg('US');
82
- expect(html).toContain('title="United States of America"');
83
- });
84
-
85
- test('should wrap img in span', () => {
86
- const html = Country.getFlagImg('GB');
87
- expect(html).toMatch(/^<span>/);
88
- expect(html).toMatch(/<\/span>$/);
83
+ expect(html).toContain("title=\"France\"");
84
+ Country.flagsPath = undefined;
89
85
  });
90
86
  });
91
87
 
@@ -129,6 +125,74 @@ describe('Country', () => {
129
125
  });
130
126
  });
131
127
 
128
+ describe('Locale', () => {
129
+ describe('normalize', () => {
130
+ test('should canonicalize to lang-REGION format', () => {
131
+ expect(Locale.normalize('fr-FR')).toBe('fr-FR');
132
+ expect(Locale.normalize('fr-fr')).toBe('fr-FR');
133
+ expect(Locale.normalize('FR-fr')).toBe('fr-FR');
134
+ expect(Locale.normalize('fr_FR')).toBe('fr-FR');
135
+ });
136
+
137
+ test('should handle lang-only locales', () => {
138
+ expect(Locale.normalize('fr')).toBe('fr');
139
+ expect(Locale.normalize('EN')).toBe('en');
140
+ });
141
+ });
142
+
143
+ describe('getBaseLang', () => {
144
+ test('should extract base language', () => {
145
+ expect(Locale.getBaseLang('fr-FR')).toBe('fr');
146
+ expect(Locale.getBaseLang('en-US')).toBe('en');
147
+ expect(Locale.getBaseLang('fr_FR')).toBe('fr');
148
+ });
149
+
150
+ test('should return lang as-is when no region', () => {
151
+ expect(Locale.getBaseLang('fr')).toBe('fr');
152
+ });
153
+ });
154
+
155
+ describe('getRegion', () => {
156
+ test('should extract region code', () => {
157
+ expect(Locale.getRegion('fr-FR')).toBe('FR');
158
+ expect(Locale.getRegion('en-US')).toBe('US');
159
+ expect(Locale.getRegion('fr_FR')).toBe('FR');
160
+ });
161
+
162
+ test('should return null when no region', () => {
163
+ expect(Locale.getRegion('fr')).toBeNull();
164
+ });
165
+ });
166
+
167
+ describe('isValid', () => {
168
+ test('should accept valid locales', () => {
169
+ expect(Locale.isValid('fr')).toBe(true);
170
+ expect(Locale.isValid('fr-FR')).toBe(true);
171
+ expect(Locale.isValid('en-US')).toBe(true);
172
+ expect(Locale.isValid('zh-CN')).toBe(true);
173
+ });
174
+
175
+ test('should reject invalid locales', () => {
176
+ expect(Locale.isValid('foobar')).toBe(false);
177
+ expect(Locale.isValid('fr_FR')).toBe(false);
178
+ expect(Locale.isValid('')).toBe(false);
179
+ expect(Locale.isValid('1234')).toBe(false);
180
+ });
181
+ });
182
+
183
+ describe('toPOSIX', () => {
184
+ test('should convert to POSIX format', () => {
185
+ expect(Locale.toPOSIX('fr-FR')).toBe('fr_FR');
186
+ expect(Locale.toPOSIX('en-US')).toBe('en_US');
187
+ expect(Locale.toPOSIX('fr_FR')).toBe('fr_FR');
188
+ });
189
+
190
+ test('should handle lang-only locales', () => {
191
+ expect(Locale.toPOSIX('fr')).toBe('fr');
192
+ });
193
+ });
194
+ });
195
+
132
196
  describe('GeographicCoordinates', () => {
133
197
  describe('check', () => {
134
198
  test('should validate correct coordinates', () => {