@osimatic/helpers-js 1.5.28 → 1.5.30

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/bank.js CHANGED
@@ -15,7 +15,7 @@ class BankCard {
15
15
  return cardNumber;
16
16
  }
17
17
 
18
- static formatExpirationDate(expirationDate, locale="fr-FR") {
18
+ static formatExpirationDate(expirationDate, locale='fr-FR') {
19
19
  return SqlDateTime.getMonthName(expirationDate, locale)+' '+SqlDateTime.getYear(expirationDate);
20
20
  }
21
21
  }
package/date_time.js CHANGED
@@ -100,27 +100,27 @@ class DateTime {
100
100
  return Math.trunc(jsDate.getTime()/1000);
101
101
  }
102
102
 
103
- static getDateDigitalDisplay(jsDate, locale="fr-FR", timeZone="Europe/Paris") {
103
+ static getDateDigitalDisplay(jsDate, locale='fr-FR', timeZone="Europe/Paris") {
104
104
  return DateTimeFormatter.getDateDigitalFormatter(locale, timeZone).format(jsDate);
105
105
  //return jsDate.toLocaleDateString(locale, {year: 'numeric', month: 'numeric', day: 'numeric', timeZone: timeZone});
106
106
  }
107
107
 
108
- static getDateTextDisplay(jsDate, locale="fr-FR", timeZone="Europe/Paris") {
108
+ static getDateTextDisplay(jsDate, locale='fr-FR', timeZone="Europe/Paris") {
109
109
  return DateTimeFormatter.getDateTextFormatter(locale, timeZone).format(jsDate);
110
110
  //return jsDate.toLocaleDateString(locale, {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: timeZone});
111
111
  }
112
112
 
113
- static getTimeDisplay(jsDate, locale="fr-FR", timeZone="Europe/Paris") {
113
+ static getTimeDisplay(jsDate, locale='fr-FR', timeZone="Europe/Paris") {
114
114
  return DateTimeFormatter.getTimeFormatter(locale, timeZone).format(jsDate);
115
115
  //return jsDate.toLocaleTimeString(locale, {hour: 'numeric', minute: 'numeric', timeZone: timeZone});
116
116
  }
117
117
 
118
- static getTimeDigitalDisplay(jsDate, locale="fr-FR", timeZone="Europe/Paris") {
118
+ static getTimeDigitalDisplay(jsDate, locale='fr-FR', timeZone="Europe/Paris") {
119
119
  return DateTimeFormatter.getTimeDigitalFormatter(locale, timeZone).format(jsDate);
120
120
  //return jsDate.toLocaleTimeString(locale, {hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: timeZone});
121
121
  }
122
122
 
123
- static getTimeDisplayWithNbDays(jsDate, jsPreviousDate, locale="fr-FR", timeZone="Europe/Paris") {
123
+ static getTimeDisplayWithNbDays(jsDate, jsPreviousDate, locale='fr-FR', timeZone="Europe/Paris") {
124
124
  let str = this.getTimeDisplay(jsDate, locale, timeZone);
125
125
  if (jsPreviousDate !== 0 && jsPreviousDate != null) {
126
126
  let nbDaysDiff = DatePeriod.getNbDayBetweenTwo(jsPreviousDate, jsDate, false);
@@ -131,7 +131,7 @@ class DateTime {
131
131
  return str;
132
132
  }
133
133
 
134
- static getDateTimeDigitalDisplay(jsDate, locale="fr-FR", timeZone="Europe/Paris") {
134
+ static getDateTimeDigitalDisplay(jsDate, locale='fr-FR', timeZone="Europe/Paris") {
135
135
  return DateTimeFormatter.getDateTimeFormatter(locale, timeZone).format(jsDate);
136
136
  //return jsDate.toLocaleDateString(locale, {year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', timeZone: timeZone});
137
137
  }
@@ -142,7 +142,7 @@ class DateTime {
142
142
  static getMonth(jsDate) {
143
143
  return jsDate.getUTCMonth()+1;
144
144
  }
145
- static getMonthName(jsDate, locale="fr-FR", isShort=false) {
145
+ static getMonthName(jsDate, locale='fr-FR', isShort=false) {
146
146
  return jsDate.toLocaleDateString(locale, {month: (isShort?'short':'long')});
147
147
  }
148
148
  static getDay(jsDate) {
@@ -157,7 +157,7 @@ class DateTime {
157
157
  return jsDate.getUTCDay();
158
158
  }
159
159
 
160
- static getDayName(jsDate, locale="fr-FR", isShort=false) {
160
+ static getDayName(jsDate, locale='fr-FR', isShort=false) {
161
161
  return jsDate.toLocaleDateString(locale, {weekday: (isShort?'short':'long')});
162
162
  }
163
163
 
@@ -165,13 +165,13 @@ class DateTime {
165
165
  return new Date(Date.UTC(year, month, 0)).getDate();
166
166
  }
167
167
 
168
- static getMonthNameByMonth(month, locale="fr-FR", isShort=false) {
168
+ static getMonthNameByMonth(month, locale='fr-FR', isShort=false) {
169
169
  let d = new Date();
170
170
  d.setDate(1);
171
171
  d.setMonth(month-1);
172
172
  return this.getMonthName(d, locale, isShort);
173
173
  }
174
- static getDayNameByDayOfWeek(dayOfWeek, locale="fr-FR", isShort=false) {
174
+ static getDayNameByDayOfWeek(dayOfWeek, locale='fr-FR', isShort=false) {
175
175
  let d = new Date();
176
176
  // d.setDate(d.getDate() + (1 + 7 - d.getDay()) % 7);
177
177
  d.setDate(d.getDate() + (dayOfWeek - d.getDay()) % 7);
@@ -375,20 +375,20 @@ class TimestampUnix {
375
375
  return parseInt(today.getTime() / 1000);
376
376
  }
377
377
 
378
- static getDateDigitalDisplay(timestamp, locale="fr-FR", timeZone="Europe/Paris") {
378
+ static getDateDigitalDisplay(timestamp, locale='fr-FR', timeZone="Europe/Paris") {
379
379
  return DateTime.getDateDigitalDisplay(this.parse(timestamp), locale, timeZone);
380
380
  }
381
- static getDateTextDisplay(timestamp, locale="fr-FR", timeZone="Europe/Paris") {
381
+ static getDateTextDisplay(timestamp, locale='fr-FR', timeZone="Europe/Paris") {
382
382
  return DateTime.getDateTextDisplay(this.parse(timestamp), locale, timeZone);
383
383
  }
384
384
 
385
- static getTimeDisplay(timestamp, locale="fr-FR", timeZone="Europe/Paris") {
385
+ static getTimeDisplay(timestamp, locale='fr-FR', timeZone="Europe/Paris") {
386
386
  return DateTime.getTimeDisplay(this.parse(timestamp), locale, timeZone);
387
387
  }
388
- static getTimeDisplayWithNbDays(timestamp, previousTimestamp, locale="fr-FR", timeZone="Europe/Paris") {
388
+ static getTimeDisplayWithNbDays(timestamp, previousTimestamp, locale='fr-FR', timeZone="Europe/Paris") {
389
389
  return DateTime.getTimeDisplayWithNbDays(this.parse(timestamp), this.parse(previousTimestamp), locale, timeZone);
390
390
  }
391
- static getTimeDigitalDisplay(timestamp, locale="fr-FR", timeZone="Europe/Paris") {
391
+ static getTimeDigitalDisplay(timestamp, locale='fr-FR', timeZone="Europe/Paris") {
392
392
  return DateTime.getTimeDigitalDisplay(this.parse(timestamp), locale, timeZone);
393
393
  }
394
394
 
@@ -477,10 +477,10 @@ class SqlDate {
477
477
  return DateTime.getSqlDate(new Date());
478
478
  }
479
479
 
480
- static getDateDigitalDisplay(sqlDate, locale="fr-FR", timeZone="Europe/Paris") {
480
+ static getDateDigitalDisplay(sqlDate, locale='fr-FR', timeZone="Europe/Paris") {
481
481
  return SqlDateTime.getDateDigitalDisplay(sqlDate+" 00:00:00", locale, timeZone);
482
482
  }
483
- static getDateTextDisplay(sqlDate, locale="fr-FR", timeZone="Europe/Paris") {
483
+ static getDateTextDisplay(sqlDate, locale='fr-FR', timeZone="Europe/Paris") {
484
484
  return SqlDateTime.getDateTextDisplay(sqlDate+" 00:00:00", locale, timeZone);
485
485
  }
486
486
 
@@ -498,7 +498,7 @@ class SqlDate {
498
498
  static getMonth(sqlDate) {
499
499
  return SqlDateTime.getMonth(sqlDate+" 00:00:00");
500
500
  }
501
- static getMonthName(sqlDate, locale="fr-FR", isShort=false) {
501
+ static getMonthName(sqlDate, locale='fr-FR', isShort=false) {
502
502
  return SqlDateTime.getMonthName(sqlDate+" 00:00:00", locale, isShort);
503
503
  }
504
504
  static getDay(sqlDate) {
@@ -539,13 +539,13 @@ class SqlTime {
539
539
  return DateTime.getSqlTime(new Date());
540
540
  }
541
541
 
542
- static getTimeDisplay(sqlTime, locale="fr-FR", timeZone="Europe/Paris") {
542
+ static getTimeDisplay(sqlTime, locale='fr-FR', timeZone="Europe/Paris") {
543
543
  return SqlDateTime.getTimeDisplay('1970-01-01 '+sqlTime, locale, timeZone);
544
544
  }
545
- static getTimeDigitalDisplay(sqlTime, locale="fr-FR", timeZone="Europe/Paris") {
545
+ static getTimeDigitalDisplay(sqlTime, locale='fr-FR', timeZone="Europe/Paris") {
546
546
  return SqlDateTime.getTimeDigitalDisplay('1970-01-01 '+sqlTime, locale, timeZone);
547
547
  }
548
- static getTimeDisplayWithNbDays(sqlTime, previousSqlTime, locale="fr-FR", timeZone="Europe/Paris") {
548
+ static getTimeDisplayWithNbDays(sqlTime, previousSqlTime, locale='fr-FR', timeZone="Europe/Paris") {
549
549
  return SqlDateTime.getTimeDisplayWithNbDays('1970-01-01 '+sqlTime, '1970-01-01 '+previousSqlTime, locale, timeZone);
550
550
  }
551
551
 
@@ -588,24 +588,24 @@ class SqlDateTime {
588
588
  return new Date(Date.UTC(sqlDateTime.substring(0, 4), sqlDateTime.substring(5, 7)-1, sqlDateTime.substring(8, 10), sqlDateTime.substring(11, 13), sqlDateTime.substring(14, 16), sqlDateTime.substring(17, 19)));
589
589
  }
590
590
 
591
- static getDateDigitalDisplay(sqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
591
+ static getDateDigitalDisplay(sqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
592
592
  return DateTime.getDateDigitalDisplay(this.parse(sqlDateTime), locale, timeZone);
593
593
  }
594
- static getDateTextDisplay(sqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
594
+ static getDateTextDisplay(sqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
595
595
  return DateTime.getDateTextDisplay(this.parse(sqlDateTime), locale, timeZone);
596
596
  }
597
597
 
598
- static getTimeDisplay(sqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
598
+ static getTimeDisplay(sqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
599
599
  return DateTime.getTimeDisplay(this.parse(sqlDateTime), locale, timeZone);
600
600
  }
601
- static getTimeDisplayWithNbDays(sqlDateTime, previousSqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
601
+ static getTimeDisplayWithNbDays(sqlDateTime, previousSqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
602
602
  return DateTime.getTimeDisplayWithNbDays(this.parse(sqlDateTime), this.parse(previousSqlDateTime), locale, timeZone);
603
603
  }
604
- static getTimeDigitalDisplay(sqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
604
+ static getTimeDigitalDisplay(sqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
605
605
  return DateTime.getTimeDigitalDisplay(this.parse(sqlDateTime), locale, timeZone);
606
606
  }
607
607
 
608
- static getDateTimeDigitalDisplay(sqlDateTime, locale="fr-FR", timeZone="Europe/Paris") {
608
+ static getDateTimeDigitalDisplay(sqlDateTime, locale='fr-FR', timeZone="Europe/Paris") {
609
609
  return DateTime.getDateTimeDigitalDisplay(this.parse(sqlDateTime), locale, timeZone);
610
610
  }
611
611
 
@@ -622,7 +622,7 @@ class SqlDateTime {
622
622
  static getMonth(sqlDateTime) {
623
623
  return DateTime.getMonth(this.parse(sqlDateTime));
624
624
  }
625
- static getMonthName(sqlDateTime, locale="fr-FR", isShort=false) {
625
+ static getMonthName(sqlDateTime, locale='fr-FR', isShort=false) {
626
626
  return DateTime.getMonthName(this.parse(sqlDateTime), locale);
627
627
  }
628
628
  static getDay(sqlDateTime) {
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 fillCountrySelect(select, defaultValue=null, countriesList=null, addNoneValue=false, noneLabel='- Aucun -', showFlags=false) {
27
+ static fillSelect(select, defaultValue=null, showFlags=false, countriesList=null, addNoneValue=false, noneLabel='- Aucun -', locale='fr-FR') {
24
28
  select = toEl(select);
25
29
  if (!select) {
26
30
  return;
@@ -29,21 +33,49 @@ class Country {
29
33
  if (addNoneValue) {
30
34
  select.insertAdjacentHTML('beforeend', '<option value="">'+noneLabel+'</option>');
31
35
  }
32
- Object.entries(null != countriesList ? countriesList : Country.getCountries()).forEach(([countryCode, countryName]) => {
33
- const flag = showFlags ? Country.getFlagEmoji(countryCode) + ' ' : '';
34
- select.insertAdjacentHTML('beforeend', '<option value="' + countryCode + '">' + flag + countryName + '</option>');
36
+ const allCountries = Country.getCountries(locale);
37
+ const entries = countriesList != null
38
+ ? countriesList.map(code => [code, allCountries[code] || code])
39
+ : Object.entries(allCountries);
40
+ entries.forEach(([countryCode, countryName]) => {
41
+ let attrs = '';
42
+ if (showFlags) {
43
+ if (typeof Country.flagsPath !== 'undefined') {
44
+ attrs = ' data-thumbnail="' + Country.getFlagPath(countryCode) + '"';
45
+ } else {
46
+ attrs = ' data-content="<span class=&quot;fi fi-' + countryCode.toLowerCase() + '&quot;></span> ' + countryName + '"';
47
+ }
48
+ }
49
+ select.insertAdjacentHTML('beforeend', '<option value="' + countryCode + '"' + attrs + '>' + countryName + '</option>');
35
50
  });
36
51
  }
37
52
  if (null != defaultValue) {
38
53
  select.value = defaultValue;
39
54
  }
40
55
  }
41
- static getCountryName(countryCode, locale = 'en') {
42
- const name = isoCountries.getName(countryCode, locale);
56
+
57
+ static fillSelectWithFlags(select, defaultValue=null, countriesList=null, addNoneValue=false, noneLabel='- Aucun -', locale='fr-FR') {
58
+ return Country.fillSelect(select, defaultValue, true, countriesList, addNoneValue, noneLabel, locale);
59
+ }
60
+
61
+ static getCountryName(countryCode, locale='fr-FR') {
62
+ const baseLang = Locale.getBaseLang(locale);
63
+ Country.getCountries(locale); // ensure locale is registered
64
+ const name = isoCountries.getName(countryCode, baseLang);
43
65
  return name || countryCode;
44
66
  }
45
67
  static getCountries(locale = 'en') {
46
- return isoCountries.getNames(locale);
68
+ const baseLang = Locale.getBaseLang(locale);
69
+ let names = isoCountries.getNames(baseLang);
70
+ if (!names || Object.keys(names).length === 0) {
71
+ try {
72
+ isoCountries.registerLocale(require('i18n-iso-countries/langs/' + baseLang + '.json'));
73
+ names = isoCountries.getNames(baseLang);
74
+ } catch (e) {
75
+ // locale not available
76
+ }
77
+ }
78
+ return names || {};
47
79
  }
48
80
 
49
81
  static getContinents() {
@@ -58,10 +90,28 @@ class Country {
58
90
  };
59
91
  }
60
92
 
61
- /** @deprecated **/
93
+ /** @deprecated Use getCountries instead **/
62
94
  static getCountryList() {
63
95
  return Country.getCountries();
64
96
  }
97
+ /** @deprecated Use fillSelect instead **/
98
+ static fillCountrySelect(select, defaultValue=null, countriesList=null, addNoneValue=false, noneLabel='- Aucun -') {
99
+ select = toEl(select);
100
+ if (!select) {
101
+ return;
102
+ }
103
+ if (select.children.length === 0) {
104
+ if (addNoneValue) {
105
+ select.insertAdjacentHTML('beforeend', '<option value="">'+noneLabel+'</option>');
106
+ }
107
+ Object.entries(null != countriesList ? countriesList : Country.getCountries()).forEach(([countryCode, countryName]) => {
108
+ select.insertAdjacentHTML('beforeend', '<option value="' + countryCode + '">' + countryName + '</option>');
109
+ });
110
+ }
111
+ if (null != defaultValue) {
112
+ select.value = defaultValue;
113
+ }
114
+ }
65
115
  }
66
116
 
67
117
  class PostalAddress {
@@ -487,4 +537,35 @@ class GeographicCoordinates {
487
537
 
488
538
  }
489
539
 
490
- module.exports = { Country, PostalAddress, GeographicCoordinates, Polygon };
540
+ class Locale {
541
+ static getBaseLang(locale) {
542
+ return Locale.normalize(locale).split('-')[0];
543
+ }
544
+
545
+ static getRegion(locale) {
546
+ const parts = Locale.normalize(locale).split('-');
547
+ return parts.length > 1 ? parts[1] : null;
548
+ }
549
+
550
+ static normalize(locale) {
551
+ // Accept both 'fr_FR' and 'fr-fr', output 'fr-FR'
552
+ const parts = locale.replace('_', '-').split('-');
553
+ const lang = parts[0].toLowerCase();
554
+ const region = parts[1] ? parts[1].toUpperCase() : null;
555
+ return region ? lang + '-' + region : lang;
556
+ }
557
+
558
+ static isValid(locale) {
559
+ return /^[a-zA-Z]{2,3}(-[a-zA-Z]{2,3})?$/.test(locale);
560
+ }
561
+
562
+ static toPOSIX(locale) {
563
+ return Locale.normalize(locale).replace('-', '_');
564
+ }
565
+
566
+ static update(locale) {
567
+ HTTPClient.setHeader('Accept-Language', Locale.normalize(locale));
568
+ }
569
+ }
570
+
571
+ 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.28",
3
+ "version": "1.5.30",
4
4
  "main": "main.js",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -21,6 +21,7 @@
21
21
  },
22
22
  "peerDependencies": {
23
23
  "datatables.net": ">=2.0",
24
+ "flag-icons": ">=7.0",
24
25
  "intl-tel-input": ">=19.0",
25
26
  "leaflet": ">=1.9",
26
27
  "leaflet-draw": ">=1.0",
@@ -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,17 @@ 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
+
28
33
  test('should return the code itself if country not found', () => {
29
34
  expect(Country.getCountryName('XX')).toBe('XX');
30
35
  expect(Country.getCountryName('ZZZ')).toBe('ZZZ');
@@ -42,50 +47,35 @@ describe('Country', () => {
42
47
  });
43
48
 
44
49
  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');
50
+ test('should return null when no flagsPath set', () => {
51
+ Country.flagsPath = undefined;
52
+ expect(Country.getFlagPath('FR')).toBeNull();
53
53
  });
54
54
 
55
- test('should use custom flags path when set', () => {
55
+ test('should return image path when flagsPath set', () => {
56
56
  Country.setFlagsPath('/custom/flags/');
57
- const path = Country.getFlagPath('FR');
58
- expect(path).toBe('/custom/flags/fr.png');
59
-
60
- // Reset for other tests
57
+ expect(Country.getFlagPath('FR')).toBe('/custom/flags/fr.png');
58
+ expect(Country.getFlagPath('US')).toBe('/custom/flags/us.png');
61
59
  Country.flagsPath = undefined;
62
60
  });
61
+ });
63
62
 
64
- test('should use default path when not set', () => {
63
+ describe('getFlagImg', () => {
64
+ test('should return flag-icons span when no flagsPath set', () => {
65
65
  Country.flagsPath = undefined;
66
- const path = Country.getFlagPath('DE');
67
- expect(path).toBe('/de.png');
66
+ const html = Country.getFlagImg('FR');
67
+ expect(html).toContain('<span');
68
+ expect(html).toContain('fi fi-fr');
68
69
  });
69
- });
70
70
 
71
- describe('getFlagImg', () => {
72
- test('should return HTML img tag', () => {
71
+ test('should return img tag when flagsPath set', () => {
72
+ Country.setFlagsPath('/flags/');
73
73
  const html = Country.getFlagImg('FR');
74
74
  expect(html).toContain('<img');
75
- expect(html).toContain('src=');
76
75
  expect(html).toContain('fr.png');
77
76
  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>$/);
77
+ expect(html).toContain("title=\"France\"");
78
+ Country.flagsPath = undefined;
89
79
  });
90
80
  });
91
81
 
@@ -109,13 +99,91 @@ describe('Country', () => {
109
99
  });
110
100
 
111
101
  describe('getCountries with locale', () => {
112
- test('should support locale parameter', () => {
113
- const isoCountries = require('i18n-iso-countries');
114
- isoCountries.registerLocale(require('i18n-iso-countries/langs/fr.json'));
102
+ test('should auto-register and return names for an unregistered locale', () => {
115
103
  const countriesFr = Country.getCountries('fr');
104
+ expect(typeof countriesFr).toBe('object');
116
105
  expect(countriesFr.FR).toBe('France');
117
106
  expect(typeof countriesFr.US).toBe('string');
118
107
  });
108
+
109
+ test('should return names for de locale', () => {
110
+ const countriesDe = Country.getCountries('de');
111
+ expect(typeof countriesDe).toBe('object');
112
+ expect(countriesDe.DE).toBeTruthy();
113
+ });
114
+
115
+ test('should return empty object for unknown locale', () => {
116
+ const result = Country.getCountries('xx');
117
+ expect(typeof result).toBe('object');
118
+ });
119
+ });
120
+ });
121
+
122
+ describe('Locale', () => {
123
+ describe('normalize', () => {
124
+ test('should canonicalize to lang-REGION format', () => {
125
+ expect(Locale.normalize('fr-FR')).toBe('fr-FR');
126
+ expect(Locale.normalize('fr-fr')).toBe('fr-FR');
127
+ expect(Locale.normalize('FR-fr')).toBe('fr-FR');
128
+ expect(Locale.normalize('fr_FR')).toBe('fr-FR');
129
+ });
130
+
131
+ test('should handle lang-only locales', () => {
132
+ expect(Locale.normalize('fr')).toBe('fr');
133
+ expect(Locale.normalize('EN')).toBe('en');
134
+ });
135
+ });
136
+
137
+ describe('getBaseLang', () => {
138
+ test('should extract base language', () => {
139
+ expect(Locale.getBaseLang('fr-FR')).toBe('fr');
140
+ expect(Locale.getBaseLang('en-US')).toBe('en');
141
+ expect(Locale.getBaseLang('fr_FR')).toBe('fr');
142
+ });
143
+
144
+ test('should return lang as-is when no region', () => {
145
+ expect(Locale.getBaseLang('fr')).toBe('fr');
146
+ });
147
+ });
148
+
149
+ describe('getRegion', () => {
150
+ test('should extract region code', () => {
151
+ expect(Locale.getRegion('fr-FR')).toBe('FR');
152
+ expect(Locale.getRegion('en-US')).toBe('US');
153
+ expect(Locale.getRegion('fr_FR')).toBe('FR');
154
+ });
155
+
156
+ test('should return null when no region', () => {
157
+ expect(Locale.getRegion('fr')).toBeNull();
158
+ });
159
+ });
160
+
161
+ describe('isValid', () => {
162
+ test('should accept valid locales', () => {
163
+ expect(Locale.isValid('fr')).toBe(true);
164
+ expect(Locale.isValid('fr-FR')).toBe(true);
165
+ expect(Locale.isValid('en-US')).toBe(true);
166
+ expect(Locale.isValid('zh-CN')).toBe(true);
167
+ });
168
+
169
+ test('should reject invalid locales', () => {
170
+ expect(Locale.isValid('foobar')).toBe(false);
171
+ expect(Locale.isValid('fr_FR')).toBe(false);
172
+ expect(Locale.isValid('')).toBe(false);
173
+ expect(Locale.isValid('1234')).toBe(false);
174
+ });
175
+ });
176
+
177
+ describe('toPOSIX', () => {
178
+ test('should convert to POSIX format', () => {
179
+ expect(Locale.toPOSIX('fr-FR')).toBe('fr_FR');
180
+ expect(Locale.toPOSIX('en-US')).toBe('en_US');
181
+ expect(Locale.toPOSIX('fr_FR')).toBe('fr_FR');
182
+ });
183
+
184
+ test('should handle lang-only locales', () => {
185
+ expect(Locale.toPOSIX('fr')).toBe('fr');
186
+ });
119
187
  });
120
188
  });
121
189