Skip to main content

visi_core/core/
locale.rs

1use serde::{Deserialize, Serialize};
2
3/// Date field ordering of a locale.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub enum DateOrder {
6    /// Month-Day-Year (e.g. `en-US`: `06/22/2026`).
7    Mdy,
8    /// Day-Month-Year (e.g. `en-GB`, `de-DE`, `fr-FR`: `22/06/2026`).
9    Dmy,
10    /// Year-Month-Day (e.g. `zh-CN`, `ja-JP`, ISO: `2026-06-22`).
11    Ymd,
12}
13
14impl std::fmt::Display for DateOrder {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            DateOrder::Mdy => write!(f, "MDY"),
18            DateOrder::Dmy => write!(f, "DMY"),
19            DateOrder::Ymd => write!(f, "YMD"),
20        }
21    }
22}
23
24/// A spreadsheet regional locale configuring date and number parsing rules.
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct Locale {
27    /// Language / country tag (e.g. `"en-US"`, `"en-GB"`, `"de-DE"`, `"fr-FR"`).
28    pub code: String,
29    /// Primary date field ordering.
30    pub date_order: DateOrder,
31    /// Primary date separator (`'/'`, `'.' `, or `'-'`).
32    pub primary_date_sep: char,
33    /// Windows LCID (e.g., `0x0409` for en-US, `0x0809` for en-GB, `0x0407` for de-DE).
34    pub lcid: u16,
35    /// 2-digit year pivot threshold (default 29 -> 00..29 is 20xx, 30..99 is 19xx).
36    pub two_digit_year_pivot: u32,
37    /// Default calendar year for 2-part dates (if `None`, defaults to 2026).
38    pub default_year: Option<i32>,
39}
40
41impl Default for Locale {
42    fn default() -> Self {
43        Self::en_us()
44    }
45}
46
47impl Locale {
48    /// English (United States) locale: `MDY`, separator `/`, LCID `0x0409`.
49    pub fn en_us() -> Self {
50        Self {
51            code: "en-US".to_string(),
52            date_order: DateOrder::Mdy,
53            primary_date_sep: '/',
54            lcid: 0x0409,
55            two_digit_year_pivot: 29,
56            default_year: None,
57        }
58    }
59
60    /// English (United Kingdom) locale: `DMY`, separator `/`, LCID `0x0809`.
61    pub fn en_gb() -> Self {
62        Self {
63            code: "en-GB".to_string(),
64            date_order: DateOrder::Dmy,
65            primary_date_sep: '/',
66            lcid: 0x0809,
67            two_digit_year_pivot: 29,
68            default_year: None,
69        }
70    }
71
72    /// German (Germany) locale: `DMY`, separator `.`, LCID `0x0407`.
73    pub fn de_de() -> Self {
74        Self {
75            code: "de-DE".to_string(),
76            date_order: DateOrder::Dmy,
77            primary_date_sep: '.',
78            lcid: 0x0407,
79            two_digit_year_pivot: 29,
80            default_year: None,
81        }
82    }
83
84    /// French (France) locale: `DMY`, separator `/`, LCID `0x040C`.
85    pub fn fr_fr() -> Self {
86        Self {
87            code: "fr-FR".to_string(),
88            date_order: DateOrder::Dmy,
89            primary_date_sep: '/',
90            lcid: 0x040C,
91            two_digit_year_pivot: 29,
92            default_year: None,
93        }
94    }
95
96    /// Spanish (Spain) locale: `DMY`, separator `/`, LCID `0x0C0A`.
97    pub fn es_es() -> Self {
98        Self {
99            code: "es-ES".to_string(),
100            date_order: DateOrder::Dmy,
101            primary_date_sep: '/',
102            lcid: 0x0C0A,
103            two_digit_year_pivot: 29,
104            default_year: None,
105        }
106    }
107
108    /// Italian (Italy) locale: `DMY`, separator `/`, LCID `0x0410`.
109    pub fn it_it() -> Self {
110        Self {
111            code: "it-IT".to_string(),
112            date_order: DateOrder::Dmy,
113            primary_date_sep: '/',
114            lcid: 0x0410,
115            two_digit_year_pivot: 29,
116            default_year: None,
117        }
118    }
119
120    /// Portuguese (Brazil) locale: `DMY`, separator `/`, LCID `0x0416`.
121    pub fn pt_br() -> Self {
122        Self {
123            code: "pt-BR".to_string(),
124            date_order: DateOrder::Dmy,
125            primary_date_sep: '/',
126            lcid: 0x0416,
127            two_digit_year_pivot: 29,
128            default_year: None,
129        }
130    }
131
132    /// Dutch (Netherlands) locale: `DMY`, separator `-`, LCID `0x0413`.
133    pub fn nl_nl() -> Self {
134        Self {
135            code: "nl-NL".to_string(),
136            date_order: DateOrder::Dmy,
137            primary_date_sep: '-',
138            lcid: 0x0413,
139            two_digit_year_pivot: 29,
140            default_year: None,
141        }
142    }
143
144    /// Russian (Russia) locale: `DMY`, separator `.`, LCID `0x0419`.
145    pub fn ru_ru() -> Self {
146        Self {
147            code: "ru-RU".to_string(),
148            date_order: DateOrder::Dmy,
149            primary_date_sep: '.',
150            lcid: 0x0419,
151            two_digit_year_pivot: 29,
152            default_year: None,
153        }
154    }
155
156    /// Chinese (Simplified, China) locale: `YMD`, separator `/`, LCID `0x0804`.
157    pub fn zh_cn() -> Self {
158        Self {
159            code: "zh-CN".to_string(),
160            date_order: DateOrder::Ymd,
161            primary_date_sep: '/',
162            lcid: 0x0804,
163            two_digit_year_pivot: 29,
164            default_year: None,
165        }
166    }
167
168    /// Japanese (Japan) locale: `YMD`, separator `/`, LCID `0x0411`.
169    pub fn ja_jp() -> Self {
170        Self {
171            code: "ja-JP".to_string(),
172            date_order: DateOrder::Ymd,
173            primary_date_sep: '/',
174            lcid: 0x0411,
175            two_digit_year_pivot: 29,
176            default_year: None,
177        }
178    }
179
180    /// Look up or construct a locale from a BCP 47 or POSIX language tag.
181    ///
182    /// Recognizes tags like `"en-US"`, `"en_US"`, `"en-GB"`, `"de"`, `"de-DE"`,
183    /// `"fr"`, `"es"`, `"it"`, `"pt"`, `"nl"`, `"ru"`, `"zh"`, `"ja"`, etc.
184    pub fn from_code(code: &str) -> Option<Self> {
185        let norm = code.trim().replace('_', "-").to_lowercase();
186        if norm.starts_with("en-gb")
187            || norm.starts_with("en-au")
188            || norm.starts_with("en-nz")
189            || norm.starts_with("en-ie")
190        {
191            Some(Self::en_gb())
192        } else if norm.starts_with("en") {
193            Some(Self::en_us())
194        } else if norm.starts_with("de") {
195            Some(Self::de_de())
196        } else if norm.starts_with("fr") {
197            Some(Self::fr_fr())
198        } else if norm.starts_with("es") {
199            Some(Self::es_es())
200        } else if norm.starts_with("it") {
201            Some(Self::it_it())
202        } else if norm.starts_with("pt") {
203            Some(Self::pt_br())
204        } else if norm.starts_with("nl") {
205            Some(Self::nl_nl())
206        } else if norm.starts_with("ru") {
207            Some(Self::ru_ru())
208        } else if norm.starts_with("zh") {
209            Some(Self::zh_cn())
210        } else if norm.starts_with("ja") {
211            Some(Self::ja_jp())
212        } else {
213            None
214        }
215    }
216
217    /// The default year for 2-part dates (e.g. `6/22` or `22-Jun`).
218    pub fn default_year(&self) -> i32 {
219        self.default_year.unwrap_or(2026)
220    }
221
222    /// Convert a 2-digit year to a 4-digit year using this locale's pivot.
223    pub fn expand_two_digit_year(&self, year: i32) -> i32 {
224        if (0..=self.two_digit_year_pivot as i32).contains(&year) {
225            2000 + year
226        } else if (0..100).contains(&year) {
227            1900 + year
228        } else {
229            year
230        }
231    }
232
233    /// Attempts to match a month name/word token against this locale's month
234    /// dictionaries (or English as fallback). Returns `(month_1_based, is_full_name)`.
235    pub fn match_month_word(&self, word: &str) -> Option<(u32, bool)> {
236        let cleaned = word.trim().trim_end_matches('.');
237        if cleaned.is_empty() {
238            return None;
239        }
240
241        let lang = self.code.split('-').next().unwrap_or("en").to_lowercase();
242        if let Some(res) = match_month_for_lang(&lang, cleaned) {
243            return Some(res);
244        }
245
246        if lang != "en"
247            && let Some(res) = match_month_for_lang("en", cleaned)
248        {
249            return Some(res);
250        }
251
252        None
253    }
254}
255
256struct MonthEntry {
257    name: &'static str,
258    month: u32,
259    full: bool,
260}
261
262fn normalize_str(s: &str) -> String {
263    let mut out = String::with_capacity(s.len());
264    for c in s.chars() {
265        match c.to_ascii_lowercase() {
266            'é' | 'è' | 'ê' | 'ë' => out.push('e'),
267            'á' | 'à' | 'â' | 'ä' | 'ã' => out.push('a'),
268            'í' | 'ì' | 'î' | 'ï' => out.push('i'),
269            'ó' | 'ò' | 'ô' | 'ö' | 'õ' => out.push('o'),
270            'ú' | 'ù' | 'û' | 'ü' => out.push('u'),
271            'ñ' => out.push('n'),
272            'ç' => out.push('c'),
273            other => out.push(other),
274        }
275    }
276    out
277}
278
279fn match_month_for_lang(lang: &str, word: &str) -> Option<(u32, bool)> {
280    let norm = normalize_str(word);
281    let entries = get_month_entries_for_lang(lang);
282    for entry in entries {
283        if normalize_str(entry.name) == norm {
284            return Some((entry.month, entry.full));
285        }
286    }
287    None
288}
289
290fn get_month_entries_for_lang(lang: &str) -> &'static [MonthEntry] {
291    match lang {
292        "de" => &GERMAN_MONTHS,
293        "fr" => &FRENCH_MONTHS,
294        "es" => &SPANISH_MONTHS,
295        "it" => &ITALIAN_MONTHS,
296        "pt" => &PORTUGUESE_MONTHS,
297        "nl" => &DUTCH_MONTHS,
298        "ru" => &RUSSIAN_MONTHS,
299        "zh" | "ja" => &CJK_MONTHS,
300        _ => &ENGLISH_MONTHS,
301    }
302}
303
304const ENGLISH_MONTHS: [MonthEntry; 24] = [
305    MonthEntry {
306        name: "january",
307        month: 1,
308        full: true,
309    },
310    MonthEntry {
311        name: "february",
312        month: 2,
313        full: true,
314    },
315    MonthEntry {
316        name: "march",
317        month: 3,
318        full: true,
319    },
320    MonthEntry {
321        name: "april",
322        month: 4,
323        full: true,
324    },
325    MonthEntry {
326        name: "may",
327        month: 5,
328        full: true,
329    },
330    MonthEntry {
331        name: "june",
332        month: 6,
333        full: true,
334    },
335    MonthEntry {
336        name: "july",
337        month: 7,
338        full: true,
339    },
340    MonthEntry {
341        name: "august",
342        month: 8,
343        full: true,
344    },
345    MonthEntry {
346        name: "september",
347        month: 9,
348        full: true,
349    },
350    MonthEntry {
351        name: "october",
352        month: 10,
353        full: true,
354    },
355    MonthEntry {
356        name: "november",
357        month: 11,
358        full: true,
359    },
360    MonthEntry {
361        name: "december",
362        month: 12,
363        full: true,
364    },
365    MonthEntry {
366        name: "jan",
367        month: 1,
368        full: false,
369    },
370    MonthEntry {
371        name: "feb",
372        month: 2,
373        full: false,
374    },
375    MonthEntry {
376        name: "mar",
377        month: 3,
378        full: false,
379    },
380    MonthEntry {
381        name: "apr",
382        month: 4,
383        full: false,
384    },
385    MonthEntry {
386        name: "may",
387        month: 5,
388        full: false,
389    },
390    MonthEntry {
391        name: "jun",
392        month: 6,
393        full: false,
394    },
395    MonthEntry {
396        name: "jul",
397        month: 7,
398        full: false,
399    },
400    MonthEntry {
401        name: "aug",
402        month: 8,
403        full: false,
404    },
405    MonthEntry {
406        name: "sep",
407        month: 9,
408        full: false,
409    },
410    MonthEntry {
411        name: "oct",
412        month: 10,
413        full: false,
414    },
415    MonthEntry {
416        name: "nov",
417        month: 11,
418        full: false,
419    },
420    MonthEntry {
421        name: "dec",
422        month: 12,
423        full: false,
424    },
425];
426
427const GERMAN_MONTHS: [MonthEntry; 27] = [
428    MonthEntry {
429        name: "januar",
430        month: 1,
431        full: true,
432    },
433    MonthEntry {
434        name: "februar",
435        month: 2,
436        full: true,
437    },
438    MonthEntry {
439        name: "marz",
440        month: 3,
441        full: true,
442    },
443    MonthEntry {
444        name: "maerz",
445        month: 3,
446        full: true,
447    },
448    MonthEntry {
449        name: "april",
450        month: 4,
451        full: true,
452    },
453    MonthEntry {
454        name: "mai",
455        month: 5,
456        full: true,
457    },
458    MonthEntry {
459        name: "juni",
460        month: 6,
461        full: true,
462    },
463    MonthEntry {
464        name: "juli",
465        month: 7,
466        full: true,
467    },
468    MonthEntry {
469        name: "august",
470        month: 8,
471        full: true,
472    },
473    MonthEntry {
474        name: "september",
475        month: 9,
476        full: true,
477    },
478    MonthEntry {
479        name: "oktober",
480        month: 10,
481        full: true,
482    },
483    MonthEntry {
484        name: "november",
485        month: 11,
486        full: true,
487    },
488    MonthEntry {
489        name: "dezember",
490        month: 12,
491        full: true,
492    },
493    MonthEntry {
494        name: "jan",
495        month: 1,
496        full: false,
497    },
498    MonthEntry {
499        name: "feb",
500        month: 2,
501        full: false,
502    },
503    MonthEntry {
504        name: "mar",
505        month: 3,
506        full: false,
507    },
508    MonthEntry {
509        name: "mrz",
510        month: 3,
511        full: false,
512    },
513    MonthEntry {
514        name: "apr",
515        month: 4,
516        full: false,
517    },
518    MonthEntry {
519        name: "mai",
520        month: 5,
521        full: false,
522    },
523    MonthEntry {
524        name: "jun",
525        month: 6,
526        full: false,
527    },
528    MonthEntry {
529        name: "jul",
530        month: 7,
531        full: false,
532    },
533    MonthEntry {
534        name: "aug",
535        month: 8,
536        full: false,
537    },
538    MonthEntry {
539        name: "sep",
540        month: 9,
541        full: false,
542    },
543    MonthEntry {
544        name: "okt",
545        month: 10,
546        full: false,
547    },
548    MonthEntry {
549        name: "nov",
550        month: 11,
551        full: false,
552    },
553    MonthEntry {
554        name: "dez",
555        month: 12,
556        full: false,
557    },
558    MonthEntry {
559        name: "sept",
560        month: 9,
561        full: false,
562    },
563];
564
565const FRENCH_MONTHS: [MonthEntry; 28] = [
566    MonthEntry {
567        name: "janvier",
568        month: 1,
569        full: true,
570    },
571    MonthEntry {
572        name: "fevrier",
573        month: 2,
574        full: true,
575    },
576    MonthEntry {
577        name: "mars",
578        month: 3,
579        full: true,
580    },
581    MonthEntry {
582        name: "avril",
583        month: 4,
584        full: true,
585    },
586    MonthEntry {
587        name: "mai",
588        month: 5,
589        full: true,
590    },
591    MonthEntry {
592        name: "juin",
593        month: 6,
594        full: true,
595    },
596    MonthEntry {
597        name: "juillet",
598        month: 7,
599        full: true,
600    },
601    MonthEntry {
602        name: "aout",
603        month: 8,
604        full: true,
605    },
606    MonthEntry {
607        name: "septembre",
608        month: 9,
609        full: true,
610    },
611    MonthEntry {
612        name: "octobre",
613        month: 10,
614        full: true,
615    },
616    MonthEntry {
617        name: "novembre",
618        month: 11,
619        full: true,
620    },
621    MonthEntry {
622        name: "decembre",
623        month: 12,
624        full: true,
625    },
626    MonthEntry {
627        name: "janv",
628        month: 1,
629        full: false,
630    },
631    MonthEntry {
632        name: "jan",
633        month: 1,
634        full: false,
635    },
636    MonthEntry {
637        name: "fevr",
638        month: 2,
639        full: false,
640    },
641    MonthEntry {
642        name: "fev",
643        month: 2,
644        full: false,
645    },
646    MonthEntry {
647        name: "mar",
648        month: 3,
649        full: false,
650    },
651    MonthEntry {
652        name: "avr",
653        month: 4,
654        full: false,
655    },
656    MonthEntry {
657        name: "mai",
658        month: 5,
659        full: false,
660    },
661    MonthEntry {
662        name: "juin",
663        month: 6,
664        full: false,
665    },
666    MonthEntry {
667        name: "juil",
668        month: 7,
669        full: false,
670    },
671    MonthEntry {
672        name: "aout",
673        month: 8,
674        full: false,
675    },
676    MonthEntry {
677        name: "aou",
678        month: 8,
679        full: false,
680    },
681    MonthEntry {
682        name: "sept",
683        month: 9,
684        full: false,
685    },
686    MonthEntry {
687        name: "sep",
688        month: 9,
689        full: false,
690    },
691    MonthEntry {
692        name: "oct",
693        month: 10,
694        full: false,
695    },
696    MonthEntry {
697        name: "nov",
698        month: 11,
699        full: false,
700    },
701    MonthEntry {
702        name: "dec",
703        month: 12,
704        full: false,
705    },
706];
707
708const SPANISH_MONTHS: [MonthEntry; 26] = [
709    MonthEntry {
710        name: "enero",
711        month: 1,
712        full: true,
713    },
714    MonthEntry {
715        name: "febrero",
716        month: 2,
717        full: true,
718    },
719    MonthEntry {
720        name: "marzo",
721        month: 3,
722        full: true,
723    },
724    MonthEntry {
725        name: "abril",
726        month: 4,
727        full: true,
728    },
729    MonthEntry {
730        name: "mayo",
731        month: 5,
732        full: true,
733    },
734    MonthEntry {
735        name: "junio",
736        month: 6,
737        full: true,
738    },
739    MonthEntry {
740        name: "julio",
741        month: 7,
742        full: true,
743    },
744    MonthEntry {
745        name: "agosto",
746        month: 8,
747        full: true,
748    },
749    MonthEntry {
750        name: "septiembre",
751        month: 9,
752        full: true,
753    },
754    MonthEntry {
755        name: "setiembre",
756        month: 9,
757        full: true,
758    },
759    MonthEntry {
760        name: "octubre",
761        month: 10,
762        full: true,
763    },
764    MonthEntry {
765        name: "noviembre",
766        month: 11,
767        full: true,
768    },
769    MonthEntry {
770        name: "diciembre",
771        month: 12,
772        full: true,
773    },
774    MonthEntry {
775        name: "ene",
776        month: 1,
777        full: false,
778    },
779    MonthEntry {
780        name: "feb",
781        month: 2,
782        full: false,
783    },
784    MonthEntry {
785        name: "mar",
786        month: 3,
787        full: false,
788    },
789    MonthEntry {
790        name: "abr",
791        month: 4,
792        full: false,
793    },
794    MonthEntry {
795        name: "may",
796        month: 5,
797        full: false,
798    },
799    MonthEntry {
800        name: "jun",
801        month: 6,
802        full: false,
803    },
804    MonthEntry {
805        name: "jul",
806        month: 7,
807        full: false,
808    },
809    MonthEntry {
810        name: "ago",
811        month: 8,
812        full: false,
813    },
814    MonthEntry {
815        name: "sep",
816        month: 9,
817        full: false,
818    },
819    MonthEntry {
820        name: "set",
821        month: 9,
822        full: false,
823    },
824    MonthEntry {
825        name: "oct",
826        month: 10,
827        full: false,
828    },
829    MonthEntry {
830        name: "nov",
831        month: 11,
832        full: false,
833    },
834    MonthEntry {
835        name: "dic",
836        month: 12,
837        full: false,
838    },
839];
840
841const ITALIAN_MONTHS: [MonthEntry; 24] = [
842    MonthEntry {
843        name: "gennaio",
844        month: 1,
845        full: true,
846    },
847    MonthEntry {
848        name: "febbraio",
849        month: 2,
850        full: true,
851    },
852    MonthEntry {
853        name: "marzo",
854        month: 3,
855        full: true,
856    },
857    MonthEntry {
858        name: "aprile",
859        month: 4,
860        full: true,
861    },
862    MonthEntry {
863        name: "maggio",
864        month: 5,
865        full: true,
866    },
867    MonthEntry {
868        name: "giugno",
869        month: 6,
870        full: true,
871    },
872    MonthEntry {
873        name: "luglio",
874        month: 7,
875        full: true,
876    },
877    MonthEntry {
878        name: "agosto",
879        month: 8,
880        full: true,
881    },
882    MonthEntry {
883        name: "settembre",
884        month: 9,
885        full: true,
886    },
887    MonthEntry {
888        name: "ottobre",
889        month: 10,
890        full: true,
891    },
892    MonthEntry {
893        name: "novembre",
894        month: 11,
895        full: true,
896    },
897    MonthEntry {
898        name: "dicembre",
899        month: 12,
900        full: true,
901    },
902    MonthEntry {
903        name: "gen",
904        month: 1,
905        full: false,
906    },
907    MonthEntry {
908        name: "feb",
909        month: 2,
910        full: false,
911    },
912    MonthEntry {
913        name: "mar",
914        month: 3,
915        full: false,
916    },
917    MonthEntry {
918        name: "apr",
919        month: 4,
920        full: false,
921    },
922    MonthEntry {
923        name: "mag",
924        month: 5,
925        full: false,
926    },
927    MonthEntry {
928        name: "giu",
929        month: 6,
930        full: false,
931    },
932    MonthEntry {
933        name: "lug",
934        month: 7,
935        full: false,
936    },
937    MonthEntry {
938        name: "ago",
939        month: 8,
940        full: false,
941    },
942    MonthEntry {
943        name: "set",
944        month: 9,
945        full: false,
946    },
947    MonthEntry {
948        name: "ott",
949        month: 10,
950        full: false,
951    },
952    MonthEntry {
953        name: "nov",
954        month: 11,
955        full: false,
956    },
957    MonthEntry {
958        name: "dic",
959        month: 12,
960        full: false,
961    },
962];
963
964const PORTUGUESE_MONTHS: [MonthEntry; 24] = [
965    MonthEntry {
966        name: "janeiro",
967        month: 1,
968        full: true,
969    },
970    MonthEntry {
971        name: "fevereiro",
972        month: 2,
973        full: true,
974    },
975    MonthEntry {
976        name: "marco",
977        month: 3,
978        full: true,
979    },
980    MonthEntry {
981        name: "abril",
982        month: 4,
983        full: true,
984    },
985    MonthEntry {
986        name: "maio",
987        month: 5,
988        full: true,
989    },
990    MonthEntry {
991        name: "junho",
992        month: 6,
993        full: true,
994    },
995    MonthEntry {
996        name: "julho",
997        month: 7,
998        full: true,
999    },
1000    MonthEntry {
1001        name: "agosto",
1002        month: 8,
1003        full: true,
1004    },
1005    MonthEntry {
1006        name: "setembro",
1007        month: 9,
1008        full: true,
1009    },
1010    MonthEntry {
1011        name: "outubro",
1012        month: 10,
1013        full: true,
1014    },
1015    MonthEntry {
1016        name: "novembro",
1017        month: 11,
1018        full: true,
1019    },
1020    MonthEntry {
1021        name: "dezembro",
1022        month: 12,
1023        full: true,
1024    },
1025    MonthEntry {
1026        name: "jan",
1027        month: 1,
1028        full: false,
1029    },
1030    MonthEntry {
1031        name: "fev",
1032        month: 2,
1033        full: false,
1034    },
1035    MonthEntry {
1036        name: "mar",
1037        month: 3,
1038        full: false,
1039    },
1040    MonthEntry {
1041        name: "abr",
1042        month: 4,
1043        full: false,
1044    },
1045    MonthEntry {
1046        name: "mai",
1047        month: 5,
1048        full: false,
1049    },
1050    MonthEntry {
1051        name: "jun",
1052        month: 6,
1053        full: false,
1054    },
1055    MonthEntry {
1056        name: "jul",
1057        month: 7,
1058        full: false,
1059    },
1060    MonthEntry {
1061        name: "ago",
1062        month: 8,
1063        full: false,
1064    },
1065    MonthEntry {
1066        name: "set",
1067        month: 9,
1068        full: false,
1069    },
1070    MonthEntry {
1071        name: "out",
1072        month: 10,
1073        full: false,
1074    },
1075    MonthEntry {
1076        name: "nov",
1077        month: 11,
1078        full: false,
1079    },
1080    MonthEntry {
1081        name: "dez",
1082        month: 12,
1083        full: false,
1084    },
1085];
1086
1087const DUTCH_MONTHS: [MonthEntry; 25] = [
1088    MonthEntry {
1089        name: "januari",
1090        month: 1,
1091        full: true,
1092    },
1093    MonthEntry {
1094        name: "februari",
1095        month: 2,
1096        full: true,
1097    },
1098    MonthEntry {
1099        name: "maart",
1100        month: 3,
1101        full: true,
1102    },
1103    MonthEntry {
1104        name: "april",
1105        month: 4,
1106        full: true,
1107    },
1108    MonthEntry {
1109        name: "mei",
1110        month: 5,
1111        full: true,
1112    },
1113    MonthEntry {
1114        name: "juni",
1115        month: 6,
1116        full: true,
1117    },
1118    MonthEntry {
1119        name: "juli",
1120        month: 7,
1121        full: true,
1122    },
1123    MonthEntry {
1124        name: "augustus",
1125        month: 8,
1126        full: true,
1127    },
1128    MonthEntry {
1129        name: "september",
1130        month: 9,
1131        full: true,
1132    },
1133    MonthEntry {
1134        name: "oktober",
1135        month: 10,
1136        full: true,
1137    },
1138    MonthEntry {
1139        name: "november",
1140        month: 11,
1141        full: true,
1142    },
1143    MonthEntry {
1144        name: "december",
1145        month: 12,
1146        full: true,
1147    },
1148    MonthEntry {
1149        name: "jan",
1150        month: 1,
1151        full: false,
1152    },
1153    MonthEntry {
1154        name: "feb",
1155        month: 2,
1156        full: false,
1157    },
1158    MonthEntry {
1159        name: "mrt",
1160        month: 3,
1161        full: false,
1162    },
1163    MonthEntry {
1164        name: "mar",
1165        month: 3,
1166        full: false,
1167    },
1168    MonthEntry {
1169        name: "apr",
1170        month: 4,
1171        full: false,
1172    },
1173    MonthEntry {
1174        name: "mei",
1175        month: 5,
1176        full: false,
1177    },
1178    MonthEntry {
1179        name: "jun",
1180        month: 6,
1181        full: false,
1182    },
1183    MonthEntry {
1184        name: "jul",
1185        month: 7,
1186        full: false,
1187    },
1188    MonthEntry {
1189        name: "aug",
1190        month: 8,
1191        full: false,
1192    },
1193    MonthEntry {
1194        name: "sep",
1195        month: 9,
1196        full: false,
1197    },
1198    MonthEntry {
1199        name: "okt",
1200        month: 10,
1201        full: false,
1202    },
1203    MonthEntry {
1204        name: "nov",
1205        month: 11,
1206        full: false,
1207    },
1208    MonthEntry {
1209        name: "dec",
1210        month: 12,
1211        full: false,
1212    },
1213];
1214
1215const RUSSIAN_MONTHS: [MonthEntry; 24] = [
1216    MonthEntry {
1217        name: "январь",
1218        month: 1,
1219        full: true,
1220    },
1221    MonthEntry {
1222        name: "февраль",
1223        month: 2,
1224        full: true,
1225    },
1226    MonthEntry {
1227        name: "март",
1228        month: 3,
1229        full: true,
1230    },
1231    MonthEntry {
1232        name: "апрель",
1233        month: 4,
1234        full: true,
1235    },
1236    MonthEntry {
1237        name: "май",
1238        month: 5,
1239        full: true,
1240    },
1241    MonthEntry {
1242        name: "июнь",
1243        month: 6,
1244        full: true,
1245    },
1246    MonthEntry {
1247        name: "июль",
1248        month: 7,
1249        full: true,
1250    },
1251    MonthEntry {
1252        name: "август",
1253        month: 8,
1254        full: true,
1255    },
1256    MonthEntry {
1257        name: "сентябрь",
1258        month: 9,
1259        full: true,
1260    },
1261    MonthEntry {
1262        name: "октябрь",
1263        month: 10,
1264        full: true,
1265    },
1266    MonthEntry {
1267        name: "ноябрь",
1268        month: 11,
1269        full: true,
1270    },
1271    MonthEntry {
1272        name: "декабрь",
1273        month: 12,
1274        full: true,
1275    },
1276    MonthEntry {
1277        name: "янв",
1278        month: 1,
1279        full: false,
1280    },
1281    MonthEntry {
1282        name: "фев",
1283        month: 2,
1284        full: false,
1285    },
1286    MonthEntry {
1287        name: "мар",
1288        month: 3,
1289        full: false,
1290    },
1291    MonthEntry {
1292        name: "апр",
1293        month: 4,
1294        full: false,
1295    },
1296    MonthEntry {
1297        name: "май",
1298        month: 5,
1299        full: false,
1300    },
1301    MonthEntry {
1302        name: "июн",
1303        month: 6,
1304        full: false,
1305    },
1306    MonthEntry {
1307        name: "июл",
1308        month: 7,
1309        full: false,
1310    },
1311    MonthEntry {
1312        name: "авг",
1313        month: 8,
1314        full: false,
1315    },
1316    MonthEntry {
1317        name: "сен",
1318        month: 9,
1319        full: false,
1320    },
1321    MonthEntry {
1322        name: "окт",
1323        month: 10,
1324        full: false,
1325    },
1326    MonthEntry {
1327        name: "ноя",
1328        month: 11,
1329        full: false,
1330    },
1331    MonthEntry {
1332        name: "дек",
1333        month: 12,
1334        full: false,
1335    },
1336];
1337
1338const CJK_MONTHS: [MonthEntry; 12] = [
1339    MonthEntry {
1340        name: "1月",
1341        month: 1,
1342        full: true,
1343    },
1344    MonthEntry {
1345        name: "2月",
1346        month: 2,
1347        full: true,
1348    },
1349    MonthEntry {
1350        name: "3月",
1351        month: 3,
1352        full: true,
1353    },
1354    MonthEntry {
1355        name: "4月",
1356        month: 4,
1357        full: true,
1358    },
1359    MonthEntry {
1360        name: "5月",
1361        month: 5,
1362        full: true,
1363    },
1364    MonthEntry {
1365        name: "6月",
1366        month: 6,
1367        full: true,
1368    },
1369    MonthEntry {
1370        name: "7月",
1371        month: 7,
1372        full: true,
1373    },
1374    MonthEntry {
1375        name: "8月",
1376        month: 8,
1377        full: true,
1378    },
1379    MonthEntry {
1380        name: "9月",
1381        month: 9,
1382        full: true,
1383    },
1384    MonthEntry {
1385        name: "10月",
1386        month: 10,
1387        full: true,
1388    },
1389    MonthEntry {
1390        name: "11月",
1391        month: 11,
1392        full: true,
1393    },
1394    MonthEntry {
1395        name: "12月",
1396        month: 12,
1397        full: true,
1398    },
1399];