aboutsummaryrefslogtreecommitdiff
path: root/lib/naming/lang.ml
blob: 0d902d56266d7b7027ba35a599f2c23035d6efda (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
module OI = Colitur_kernel.Overlay_ini

module SM = Map.Make (String)

type table = string SM.t

type t = {
  code : string;
  fallback_code : string option;
  celebration : table;
  weekday : table;
  month : table;
  season : table;
  rank : table;
  colour : table;
  term : table;
  chain : t option;  (** consulted when this table misses *)
}

let empty_table = SM.empty

let rec lookup t sel key =
  match SM.find_opt key (sel t) with
  | Some v -> Some v
  | None -> ( match t.chain with Some b -> lookup b sel key | None -> None)

(* A miss returns the KEY, never "". See lang.mli for why. *)
let get t sel key = match lookup t sel key with Some v -> v | None -> key

let celebration t k = get t (fun x -> x.celebration) k
let season t k = get t (fun x -> x.season) k
let rank t k = get t (fun x -> x.rank) k
let colour t k = get t (fun x -> x.colour) k
let term t k = get t (fun x -> x.term) k

let weekday_key = [| "sunday"; "monday"; "tuesday"; "wednesday"; "thursday"; "friday"; "saturday" |]

(* weekday/month deliberately do NOT go through [get]: their SM key is a
   presentation detail (an English day-name word for weekday, a numeral for
   month), not the value a miss should degrade to. [get]'s generic
   echo-the-search-key fallback would leak that word ("sunday") out of an
   empty/raw table instead of the documented numeral -- [month] never showed
   the bug because its key already equals [string_of_int n], but [weekday]
   did, and it broke [Lang.raw]'s own identity contract (0 = Sunday, not
   "sunday"). Falling back to [string_of_int n] directly keeps [month]
   byte-identical and fixes [weekday]. *)
let weekday t n =
  if n < 0 || n > 6 then string_of_int n
  else match lookup t (fun x -> x.weekday) weekday_key.(n) with Some v -> v | None -> string_of_int n

let month t n =
  if n < 1 || n > 12 then string_of_int n
  else match lookup t (fun x -> x.month) (string_of_int n) with Some v -> v | None -> string_of_int n

let code t = t.code
let fallback_code t = t.fallback_code

let raw =
  { code = "raw"; fallback_code = None; celebration = empty_table; weekday = empty_table;
    month = empty_table; season = empty_table; rank = empty_table; colour = empty_table;
    term = empty_table; chain = None }

let with_fallback t base = { t with chain = Some base }

let of_string text =
  match OI.parse_sections text with
  | Error e -> Error e
  | Ok sections ->
      let find name =
        match List.find_opt (fun (s : OI.section) -> s.OI.name = name) sections with
        | Some s -> List.fold_left (fun m (k, v) -> SM.add k v m) empty_table s.OI.fields
        | None -> empty_table
      in
      let meta = find "meta" in
      (match SM.find_opt "lang" meta with
       | None -> Error "language file has no [meta] lang = <code>"
       | Some code ->
           Ok
             { code;
               fallback_code = SM.find_opt "fallback" meta;
               celebration = find "celebration";
               weekday = find "weekday";
               month = find "month";
               season = find "season";
               rank = find "rank";
               colour = find "colour";
               term = find "term";
               chain = None })

let keys t =
  let qualify prefix m = SM.bindings m |> List.map (fun (k, v) -> (prefix ^ "." ^ k, v)) in
  List.concat
    [ qualify "celebration" t.celebration; qualify "weekday" t.weekday;
      qualify "month" t.month; qualify "season" t.season; qualify "rank" t.rank;
      qualify "colour" t.colour; qualify "term" t.term ]
  |> List.sort compare