]> pintos-os.org Git - pspp/commitdiff
work on graphs
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 12 Jan 2026 02:57:43 +0000 (18:57 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Fri, 23 Oct 2026 20:30:01 +0000 (13:30 -0700)
151 files changed:
rust/Cargo.lock
rust/doc/book.toml
rust/doc/src/SUMMARY.md
rust/doc/src/commands/descriptives.md
rust/doc/src/commands/matrix.md
rust/doc/src/commands/set.md
rust/doc/src/invoking/output.md
rust/doc/src/invoking/pc.md [new file with mode: 0644]
rust/doc/src/invoking/por.md [new file with mode: 0644]
rust/doc/src/invoking/pspp-convert.md
rust/doc/src/invoking/pspp-show-pc.md [deleted file]
rust/doc/src/invoking/pspp-show-por.md [deleted file]
rust/doc/src/invoking/pspp-show-spv.md [deleted file]
rust/doc/src/invoking/pspp-show.md
rust/doc/src/invoking/spv.md [new file with mode: 0644]
rust/doc/src/language/basics/missing-values.md
rust/doc/src/spv/graphs.md [new file with mode: 0644]
rust/doc/src/spv/legacy-detail-xml.md
rust/doc/src/spv/structure.md
rust/pspp-derive/src/lib.rs
rust/pspp-lsp/src/main.rs
rust/pspp/.gitignore
rust/pspp/Cargo.toml
rust/pspp/README.md
rust/pspp/build.rs
rust/pspp/fuzz/fuzz_targets/fuzz_target_1.rs
rust/pspp/fuzz/fuzz_targets/segment.rs
rust/pspp/src/calendar.rs
rust/pspp/src/cli.rs
rust/pspp/src/cli/convert.rs
rust/pspp/src/cli/decrypt.rs
rust/pspp/src/cli/identify.rs
rust/pspp/src/cli/pc.rs [new file with mode: 0644]
rust/pspp/src/cli/por.rs [new file with mode: 0644]
rust/pspp/src/cli/show.rs
rust/pspp/src/cli/show_pc.rs [deleted file]
rust/pspp/src/cli/show_por.rs [deleted file]
rust/pspp/src/cli/show_spv.rs [deleted file]
rust/pspp/src/cli/spv.rs [new file with mode: 0644]
rust/pspp/src/command.rs
rust/pspp/src/command/crosstabs.rs
rust/pspp/src/command/ctables.rs
rust/pspp/src/command/data_list.rs
rust/pspp/src/command/descriptives.rs
rust/pspp/src/crypto.rs
rust/pspp/src/data.rs
rust/pspp/src/data/encoded.rs
rust/pspp/src/dictionary.rs
rust/pspp/src/endian.rs
rust/pspp/src/engine.rs
rust/pspp/src/file.rs
rust/pspp/src/file/testdata/test.stt [new file with mode: 0644]
rust/pspp/src/file/testdata/test.tlo [new file with mode: 0644]
rust/pspp/src/file/testdata/test.xml [new file with mode: 0644]
rust/pspp/src/format.rs
rust/pspp/src/format/decimals.rs [deleted file]
rust/pspp/src/format/display.rs
rust/pspp/src/format/display/tests.rs
rust/pspp/src/format/parse.rs
rust/pspp/src/hexfloat.rs
rust/pspp/src/identifier.rs
rust/pspp/src/integer.rs
rust/pspp/src/lex.rs
rust/pspp/src/lex/command_name.rs
rust/pspp/src/lex/lexer.rs
rust/pspp/src/lex/scan.rs
rust/pspp/src/lex/scan/tests.rs
rust/pspp/src/lex/segment.rs
rust/pspp/src/lex/segment/tests.rs
rust/pspp/src/lex/token.rs
rust/pspp/src/lib.rs
rust/pspp/src/locale.rs [new file with mode: 0644]
rust/pspp/src/locale/charset.rs [new file with mode: 0644]
rust/pspp/src/locale/data.rs [new file with mode: 0644]
rust/pspp/src/locale_charset.rs [deleted file]
rust/pspp/src/macros.rs
rust/pspp/src/main.rs
rust/pspp/src/message.rs
rust/pspp/src/output.rs
rust/pspp/src/output/drivers.rs
rust/pspp/src/output/drivers/cairo.rs
rust/pspp/src/output/drivers/cairo/driver.rs
rust/pspp/src/output/drivers/cairo/fsm.rs
rust/pspp/src/output/drivers/cairo/pager.rs
rust/pspp/src/output/drivers/csv.rs
rust/pspp/src/output/drivers/html.rs
rust/pspp/src/output/drivers/json.rs
rust/pspp/src/output/drivers/por.rs
rust/pspp/src/output/drivers/sav.rs
rust/pspp/src/output/drivers/spv.rs
rust/pspp/src/output/drivers/text.rs
rust/pspp/src/output/drivers/text/text_line.rs
rust/pspp/src/output/page.rs
rust/pspp/src/output/pivot.rs
rust/pspp/src/output/pivot/look.rs
rust/pspp/src/output/pivot/look_xml.rs
rust/pspp/src/output/pivot/output.rs
rust/pspp/src/output/pivot/tests.rs
rust/pspp/src/output/pivot/tlo.rs
rust/pspp/src/output/pivot/value.rs
rust/pspp/src/output/render.rs
rust/pspp/src/output/table.rs
rust/pspp/src/pc.rs
rust/pspp/src/percentiles.rs [new file with mode: 0644]
rust/pspp/src/por.rs
rust/pspp/src/por/read.rs
rust/pspp/src/por/write.rs
rust/pspp/src/prompt.rs
rust/pspp/src/settings.rs
rust/pspp/src/spv.rs
rust/pspp/src/spv/read.rs
rust/pspp/src/spv/read/css.rs
rust/pspp/src/spv/read/graph.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/format.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/frames.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/patterns.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/patterns.svg [new file with mode: 0644]
rust/pspp/src/spv/read/graph/patterns/.gitignore [new file with mode: 0644]
rust/pspp/src/spv/read/graph/style.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/symbols.rs [new file with mode: 0644]
rust/pspp/src/spv/read/graph/symbols.svg [new file with mode: 0644]
rust/pspp/src/spv/read/graph/symbols/.gitignore [new file with mode: 0644]
rust/pspp/src/spv/read/graph/var_set.rs [new file with mode: 0644]
rust/pspp/src/spv/read/html.rs
rust/pspp/src/spv/read/legacy_bin.rs
rust/pspp/src/spv/read/legacy_xml.rs
rust/pspp/src/spv/read/light.rs
rust/pspp/src/spv/read/structure.rs
rust/pspp/src/spv/read/tests.rs
rust/pspp/src/spv/testdata/.gitignore [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph1.expected.svg [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph1.xml [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph2.expected.svg [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph2.xml [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph3.expected.svg [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph3.xml [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph4.expected.svg [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph4.xml [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph5.expected.svg [new file with mode: 0644]
rust/pspp/src/spv/testdata/graph5.xml [new file with mode: 0644]
rust/pspp/src/spv/testdata/legacy-data1.bin [new file with mode: 0644]
rust/pspp/src/spv/testdata/legacy-data1.expected [new file with mode: 0644]
rust/pspp/src/spv/write.rs
rust/pspp/src/sys.rs
rust/pspp/src/sys/cooked.rs
rust/pspp/src/sys/encoding.rs
rust/pspp/src/sys/raw.rs
rust/pspp/src/sys/sack.rs
rust/pspp/src/sys/tests.rs
rust/pspp/src/sys/write.rs
rust/pspp/src/variable.rs

index dcc94b59c9d2103a617cca5b07e31c5b228d8357..28978d7597864140ecc1ed0392e0511a24777108 100644 (file)
@@ -114,6 +114,15 @@ version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "arbitrary"
 version = "1.4.1"
@@ -137,7 +146,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -159,7 +168,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -183,13 +192,28 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "bindgen"
 version = "0.72.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
  "cexpr",
  "clang-sys",
  "itertools 0.13.0",
@@ -200,14 +224,14 @@ dependencies = [
  "regex",
  "rustc-hash",
  "shlex",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
 name = "binrw"
-version = "0.14.1"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d4bca59c20d6f40c2cc0802afbe1e788b89096f61bdf7aeea6bf00f10c2909b"
+checksum = "6ad120d555272286c1017d25165ab8bd74806f13fc85b258484ec7e4ce75458f"
 dependencies = [
  "array-init",
  "binrw_derive",
@@ -216,15 +240,15 @@ dependencies = [
 
 [[package]]
 name = "binrw_derive"
-version = "0.14.1"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8ba42866ce5bced2645bfa15e97eef2c62d2bdb530510538de8dd3d04efff3c"
+checksum = "6df92e0e9baae4dc82c7bad7715ca40c0a5c71539057bf2ea04a5c29c980410b"
 dependencies = [
  "either",
  "owo-colors",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -241,9 +265,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.9.1"
+version = "2.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
 
 [[package]]
 name = "block-buffer"
@@ -254,6 +284,16 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "bstr"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
 [[package]]
 name = "btree_monstrousity"
 version = "0.0.5"
@@ -276,6 +316,18 @@ version = "1.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
 
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
 [[package]]
 name = "bytes"
 version = "1.10.1"
@@ -303,11 +355,11 @@ dependencies = [
 
 [[package]]
 name = "cairo-rs"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b58e62a27cd02fb3f63f82bb31fdda7e6c43141497cbe97e8816d7c914043f55"
+checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
  "cairo-sys-rs",
  "glib",
  "libc",
@@ -315,15 +367,21 @@ dependencies = [
 
 [[package]]
 name = "cairo-sys-rs"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "059cc746549898cbfd9a47754288e5a958756650ef4652bbb6c5f71a6bda4f8b"
+checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54"
 dependencies = [
  "glib-sys",
  "libc",
  "system-deps",
 ]
 
+[[package]]
+name = "cast"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
+
 [[package]]
 name = "cc"
 version = "1.2.24"
@@ -439,7 +497,7 @@ dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -477,6 +535,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
 [[package]]
 name = "colorchoice"
 version = "1.0.3"
@@ -513,6 +577,31 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -523,6 +612,38 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "cssparser"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa",
+ "phf",
+ "smallvec",
+]
+
+[[package]]
+name = "cssparser-color"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eeef9ae8c0e112edd89eb6406b3156ffa99c7e037b3baef1dbdf4158d35c324"
+dependencies = [
+ "cssparser",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
 [[package]]
 name = "csv"
 version = "1.3.1"
@@ -564,7 +685,7 @@ dependencies = [
  "ident_case",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -575,7 +696,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
 dependencies = [
  "darling_core",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -591,6 +712,12 @@ dependencies = [
  "parking_lot_core",
 ]
 
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
 [[package]]
 name = "dbl"
 version = "0.3.2"
@@ -623,7 +750,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -643,7 +770,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
  "unicode-xid",
 ]
 
@@ -672,7 +799,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -681,6 +808,21 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
+[[package]]
+name = "dtoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
 [[package]]
 name = "either"
 version = "1.15.0"
@@ -714,7 +856,7 @@ checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -723,7 +865,17 @@ version = "2.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
 dependencies = [
- "enum-map-derive",
+ "enum-map-derive 0.17.0",
+ "serde",
+]
+
+[[package]]
+name = "enum-map"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4532960034c15ba2f55e9eee631040ecc473c32e6c4b200828c759840f13a06"
+dependencies = [
+ "enum-map-derive 1.0.1",
  "serde",
 ]
 
@@ -735,7 +887,18 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "enum-map-derive"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7127272f64b3e098f7d81f350d672081b879bff896d219c42370dc858b1af9ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -756,7 +919,7 @@ dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -809,6 +972,15 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
 [[package]]
 name = "flate2"
 version = "1.1.1"
@@ -820,6 +992,15 @@ dependencies = [
  "miniz_oxide",
 ]
 
+[[package]]
+name = "float-cmp"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -841,6 +1022,16 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
 [[package]]
 name = "futures"
 version = "0.3.31"
@@ -896,7 +1087,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -929,6 +1120,15 @@ dependencies = [
  "slab",
 ]
 
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.14.7"
@@ -939,6 +1139,17 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
 [[package]]
 name = "getrandom"
 version = "0.3.3"
@@ -953,6 +1164,16 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "gif"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
 [[package]]
 name = "gimli"
 version = "0.31.1"
@@ -961,9 +1182,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
 [[package]]
 name = "gio"
-version = "0.20.11"
+version = "0.22.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2a5c3829f5794cb15120db87707b2ec03720edff7ad09eb7b711b532e3fe747"
+checksum = "816b6743c46b217aa8fba679095ac6f2162fd53259dc8f186fcdbff9c555db03"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -978,24 +1199,120 @@ dependencies = [
 
 [[package]]
 name = "gio-sys"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83"
+checksum = "64729ba2772c080448f9f966dba8f4456beeb100d8c28a865ef8a0f2ef4987e1"
 dependencies = [
  "glib-sys",
  "gobject-sys",
  "libc",
  "system-deps",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
+[[package]]
+name = "glam"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
+
+[[package]]
+name = "glam"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b"
+
+[[package]]
+name = "glam"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16"
+
+[[package]]
+name = "glam"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776"
+
+[[package]]
+name = "glam"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34"
+
+[[package]]
+name = "glam"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca"
+
+[[package]]
+name = "glam"
+version = "0.20.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f"
+
+[[package]]
+name = "glam"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815"
+
+[[package]]
+name = "glam"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774"
+
+[[package]]
+name = "glam"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
+
+[[package]]
+name = "glam"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945"
+
+[[package]]
+name = "glam"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
+
+[[package]]
+name = "glam"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
+
+[[package]]
+name = "glam"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
+
+[[package]]
+name = "glam"
+version = "0.29.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
+
+[[package]]
+name = "glam"
+version = "0.30.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
+
 [[package]]
 name = "glib"
-version = "0.20.10"
+version = "0.22.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2"
+checksum = "16877c6e619447e0bcb6de326a42a8bd02b36328cfeeda210135425e576efa3d"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
  "futures-channel",
  "futures-core",
  "futures-executor",
@@ -1012,22 +1329,21 @@ dependencies = [
 
 [[package]]
 name = "glib-macros"
-version = "0.20.10"
+version = "0.22.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9"
+checksum = "bda575994e3689b1bc12f89c3df621ead46ff292623b76b4710a3a5b79be54bb"
 dependencies = [
  "heck",
- "proc-macro-crate",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
 name = "glib-sys"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215"
+checksum = "48073e3b228419faa80b9b7f7122759d4ab2f44cd52a065fde7ca08f34c03147"
 dependencies = [
  "libc",
  "system-deps",
@@ -1041,9 +1357,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
 
 [[package]]
 name = "gobject-sys"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda"
+checksum = "18eda93f09d3778f38255b231b17ef67195013a592c91624a4daf8bead875565"
 dependencies = [
  "glib-sys",
  "libc",
@@ -1068,6 +1384,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
 [[package]]
 name = "heck"
 version = "0.5.0"
@@ -1260,15 +1582,44 @@ dependencies = [
  "icu_properties",
 ]
 
+[[package]]
+name = "image"
+version = "0.25.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "gif",
+ "image-webp",
+ "moxcms",
+ "num-traits",
+ "png",
+ "zune-core",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
 [[package]]
 name = "indexmap"
-version = "2.9.0"
+version = "2.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.5",
+ "hashbrown 0.16.1",
  "serde",
+ "serde_core",
 ]
 
 [[package]]
@@ -1331,7 +1682,7 @@ checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -1340,7 +1691,7 @@ version = "0.1.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
 dependencies = [
- "getrandom",
+ "getrandom 0.3.3",
  "libc",
 ]
 
@@ -1354,6 +1705,18 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "language-tags"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
 [[package]]
 name = "libc"
 version = "0.2.176"
@@ -1396,6 +1759,45 @@ version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
 
+[[package]]
+name = "librsvg"
+version = "2.62.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c343d11cf050f7c9e35aceb66f9c6818cdca6c8f322c158242dba9833e21afa5"
+dependencies = [
+ "cairo-rs",
+ "cast",
+ "cssparser",
+ "cssparser-color",
+ "data-url",
+ "encoding_rs",
+ "float-cmp",
+ "gio",
+ "glib",
+ "image",
+ "itertools 0.14.0",
+ "language-tags",
+ "libc",
+ "locale_config",
+ "markup5ever",
+ "mutants",
+ "nalgebra",
+ "num-traits",
+ "pango",
+ "pangocairo",
+ "precomputed-hash",
+ "rayon",
+ "rctree",
+ "regex",
+ "rgb",
+ "selectors",
+ "string_cache 0.9.0",
+ "system-deps",
+ "tinyvec",
+ "url",
+ "xml5ever",
+]
+
 [[package]]
 name = "libz-rs-sys"
 version = "0.5.1"
@@ -1417,6 +1819,19 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
 
+[[package]]
+name = "locale_config"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
+dependencies = [
+ "lazy_static",
+ "objc",
+ "objc-foundation",
+ "regex",
+ "winapi",
+]
+
 [[package]]
 name = "lock_api"
 version = "0.4.12"
@@ -1446,6 +1861,32 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "markup5ever"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3"
+dependencies = [
+ "log",
+ "tendril",
+ "web_atoms",
+]
+
 [[package]]
 name = "matrixmultiply"
 version = "0.3.10"
@@ -1458,9 +1899,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
 
 [[package]]
 name = "minimal-lexical"
@@ -1475,6 +1916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
 dependencies = [
  "adler2",
+ "simd-adler32",
 ]
 
 [[package]]
@@ -1488,6 +1930,65 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "moxcms"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
+[[package]]
+name = "mutants"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126"
+
+[[package]]
+name = "nalgebra"
+version = "0.34.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f"
+dependencies = [
+ "approx",
+ "glam 0.14.0",
+ "glam 0.15.2",
+ "glam 0.16.0",
+ "glam 0.17.3",
+ "glam 0.18.0",
+ "glam 0.19.0",
+ "glam 0.20.5",
+ "glam 0.21.3",
+ "glam 0.22.0",
+ "glam 0.23.0",
+ "glam 0.24.2",
+ "glam 0.25.0",
+ "glam 0.27.0",
+ "glam 0.28.0",
+ "glam 0.29.3",
+ "glam 0.30.10",
+ "matrixmultiply",
+ "nalgebra-macros",
+ "num-complex",
+ "num-rational",
+ "num-traits",
+ "simba",
+ "typenum",
+]
+
+[[package]]
+name = "nalgebra-macros"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
 [[package]]
 name = "ndarray"
 version = "0.16.1"
@@ -1503,6 +2004,12 @@ dependencies = [
  "rawpointer",
 ]
 
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
 [[package]]
 name = "nodit"
 version = "0.10.0"
@@ -1603,6 +2110,35 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
+dependencies = [
+ "block",
+ "objc",
+ "objc_id",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
 [[package]]
 name = "object"
 version = "0.36.7"
@@ -1635,15 +2171,15 @@ dependencies = [
 
 [[package]]
 name = "owo-colors"
-version = "3.5.0"
+version = "4.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
 
 [[package]]
 name = "pango"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d88d37c161f2848f0d9382597f0168484c9335ac800995f3956641abb7002938"
+checksum = "25d8f224eddef627b896d2f7b05725b3faedbd140e0e8343446f0d34f34238ee"
 dependencies = [
  "gio",
  "glib",
@@ -1653,9 +2189,9 @@ dependencies = [
 
 [[package]]
 name = "pango-sys"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "186909673fc09be354555c302c0b3dcf753cd9fa08dcb8077fa663c80fb243fa"
+checksum = "bbd111a20ca90fedf03e09c59783c679c00900f1d8491cca5399f5e33609d5d6"
 dependencies = [
  "glib-sys",
  "gobject-sys",
@@ -1665,9 +2201,9 @@ dependencies = [
 
 [[package]]
 name = "pangocairo"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58890dc451db9964ac2d8874f903a4370a4b3932aa5281ff0c8d9810937ad84f"
+checksum = "d9f15369c787b1cc59a5b86eff6afffd5a9716c5beb4969d20b307cebfe7e407"
 dependencies = [
  "cairo-rs",
  "glib",
@@ -1678,9 +2214,9 @@ dependencies = [
 
 [[package]]
 name = "pangocairo-sys"
-version = "0.20.10"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9952903f88aa93e2927e7bca2d1ebae64fc26545a9280b4ce6bddeda26b5c42"
+checksum = "d95cb73468373b9e568abb1afbaf5b42fe6ab9128fc41b5f2adbf69451c3c77f"
 dependencies = [
  "cairo-sys-rs",
  "glib-sys",
@@ -1723,6 +2259,12 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
 [[package]]
 name = "pbkdf2"
 version = "0.12.2"
@@ -1769,7 +2311,7 @@ dependencies = [
  "pest_meta",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -1782,6 +2324,67 @@ dependencies = [
  "sha2",
 ]
 
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.11.3",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
+dependencies = [
+ "phf_generator",
+ "phf_shared 0.11.3",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared 0.11.3",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
+dependencies = [
+ "siphasher",
+]
+
 [[package]]
 name = "pin-project"
 version = "1.1.10"
@@ -1799,7 +2402,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -1820,6 +2423,19 @@ version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
+[[package]]
+name = "png"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
+dependencies = [
+ "bitflags 2.11.0",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "portable-atomic"
 version = "1.11.0"
@@ -1859,6 +2475,12 @@ dependencies = [
  "zerocopy",
 ]
 
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
 [[package]]
 name = "prettyplease"
 version = "0.2.34"
@@ -1866,23 +2488,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
 dependencies = [
  "proc-macro2",
- "syn 2.0.101",
-]
-
-[[package]]
-name = "proc-macro-crate"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
-dependencies = [
- "toml_edit",
+ "syn 2.0.117",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.95"
+version = "1.0.107"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
 dependencies = [
  "unicode-ident",
 ]
@@ -1893,6 +2506,8 @@ version = "0.6.1"
 dependencies = [
  "aes",
  "anyhow",
+ "approx",
+ "base64",
  "binrw",
  "bit-vec",
  "cairo-rs",
@@ -1909,10 +2524,12 @@ dependencies = [
  "either",
  "encoding_rs",
  "enum-iterator",
- "enum-map",
+ "enum-map 2.7.3",
+ "enum-map 3.1.0",
  "enumset",
  "erased-serde",
  "flate2",
+ "gio",
  "hashbrown 0.15.5",
  "hexplay",
  "html_parser",
@@ -1920,6 +2537,7 @@ dependencies = [
  "itertools 0.14.0",
  "libc",
  "libm",
+ "librsvg",
  "ndarray",
  "nodit",
  "num",
@@ -1929,13 +2547,16 @@ dependencies = [
  "paper-sizes",
  "pspp-derive",
  "quick-xml",
- "rand",
+ "rand 0.9.1",
  "readpass",
+ "rs-stats",
  "serde",
  "serde_json",
  "serde_path_to_error",
+ "similar",
  "smallstr",
  "smallvec",
+ "svg",
  "thiserror",
  "toml 0.9.5",
  "unicase",
@@ -1954,7 +2575,7 @@ version = "0.1.0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -1968,6 +2589,21 @@ dependencies = [
  "tower-lsp",
 ]
 
+[[package]]
+name = "pxfm"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
 [[package]]
 name = "quick-xml"
 version = "0.38.4"
@@ -1980,9 +2616,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.40"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
 dependencies = [
  "proc-macro2",
 ]
@@ -1993,14 +2629,35 @@ version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
 
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
 [[package]]
 name = "rand"
 version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
 dependencies = [
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -2010,7 +2667,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.9.3",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.17",
 ]
 
 [[package]]
@@ -2019,7 +2685,7 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
 dependencies = [
- "getrandom",
+ "getrandom 0.3.3",
 ]
 
 [[package]]
@@ -2028,6 +2694,32 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
 
+[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "rctree"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e03e7866abec1101869ffa8e2c8355c4c2419d0214ece0cc3e428e5b94dea6e9"
+
 [[package]]
 name = "readpass"
 version = "1.0.3"
@@ -2045,7 +2737,7 @@ version = "0.5.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
 ]
 
 [[package]]
@@ -2077,6 +2769,32 @@ version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
+[[package]]
+name = "rgb"
+version = "0.8.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "rs-stats"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7159e98248e586cae46af3edc5bc67c5b3b670aa9ae1d37fb04c7cc5ff2aad"
+dependencies = [
+ "bincode",
+ "num",
+ "num-traits",
+ "rand 0.8.5",
+ "rand_chacha 0.3.1",
+ "rayon",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
 [[package]]
 name = "rustc-demangle"
 version = "0.1.24"
@@ -2095,7 +2813,7 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -2114,17 +2832,45 @@ version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
 
+[[package]]
+name = "safe_arch"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
+dependencies = [
+ "bytemuck",
+]
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
+[[package]]
+name = "selectors"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5685b6ae43bfcf7d2e7dfcfb5d8e8f61b46442c902531e41a32a9a8bf0ee0fb6"
+dependencies = [
+ "bitflags 2.11.0",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "new_debug_unreachable",
+ "phf",
+ "phf_codegen",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+]
+
 [[package]]
 name = "serde"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
 dependencies = [
  "serde_core",
  "serde_derive",
@@ -2132,22 +2878,22 @@ dependencies = [
 
 [[package]]
 name = "serde_core"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.228"
+version = "1.0.229"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 3.0.3",
 ]
 
 [[package]]
@@ -2182,7 +2928,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2203,6 +2949,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "servo_arc"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
+dependencies = [
+ "stable_deref_trait",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.6"
@@ -2240,12 +2995,40 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "simba"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95"
+dependencies = [
+ "approx",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "wide",
+]
+
 [[package]]
 name = "simd-adler32"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
 
+[[package]]
+name = "similar"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04d93e861ede2e497b47833469b8ec9d5c07fa4c78ce7a00f6eb7dd8168b4b3f"
+dependencies = [
+ "bstr",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
+
 [[package]]
 name = "slab"
 version = "0.4.9"
@@ -2286,6 +3069,44 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
+[[package]]
+name = "string_cache"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared 0.11.3",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared 0.13.1",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
+dependencies = [
+ "phf_generator",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+]
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -2298,11 +3119,17 @@ version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
+[[package]]
+name = "svg"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94afda9cd163c04f6bee8b4bf2501c91548deae308373c436f36aeff3cf3c4a3"
+
 [[package]]
 name = "syn"
-version = "1.0.109"
+version = "2.0.117"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2311,9 +3138,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.101"
+version = "3.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2328,7 +3155,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2350,6 +3177,17 @@ version = "0.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
 
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
 [[package]]
 name = "termcolor"
 version = "0.3.6"
@@ -2386,7 +3224,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2418,6 +3256,21 @@ dependencies = [
  "zerovec",
 ]
 
+[[package]]
+name = "tinyvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
 [[package]]
 name = "tokio"
 version = "1.45.1"
@@ -2444,7 +3297,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2584,7 +3437,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2612,7 +3465,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2696,6 +3549,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
 [[package]]
 name = "utf8_iter"
 version = "1.0.4"
@@ -2757,7 +3616,7 @@ dependencies = [
  "log",
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
  "wasm-bindgen-shared",
 ]
 
@@ -2779,7 +3638,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -2793,6 +3652,34 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "web_atoms"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414"
+dependencies = [
+ "phf",
+ "phf_codegen",
+ "string_cache 0.8.9",
+ "string_cache_codegen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
+[[package]]
+name = "wide"
+version = "0.7.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03"
+dependencies = [
+ "bytemuck",
+ "safe_arch",
+]
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -2845,7 +3732,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -2856,7 +3743,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3125,7 +4012,7 @@ version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.11.0",
 ]
 
 [[package]]
@@ -3140,6 +4027,16 @@ version = "3.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5"
 
+[[package]]
+name = "xml5ever"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee3f1e41afb31a75aef076563b0ad3ecc24f5bd9d12a72b132222664eb76b494"
+dependencies = [
+ "log",
+ "markup5ever",
+]
+
 [[package]]
 name = "yoke"
 version = "0.8.0"
@@ -3160,7 +4057,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -3181,7 +4078,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3201,7 +4098,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
  "synstructure",
 ]
 
@@ -3222,7 +4119,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3255,7 +4152,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.101",
+ "syn 2.0.117",
 ]
 
 [[package]]
@@ -3271,7 +4168,7 @@ dependencies = [
  "crc32fast",
  "deflate64",
  "flate2",
- "getrandom",
+ "getrandom 0.3.3",
  "hmac",
  "indexmap",
  "liblzma",
@@ -3329,3 +4226,18 @@ dependencies = [
  "cc",
  "pkg-config",
 ]
+
+[[package]]
+name = "zune-core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+
+[[package]]
+name = "zune-jpeg"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
+dependencies = [
+ "zune-core",
+]
index 7f9d861cd4d02a7651f71191b050e3e2c32029b1..a8acf5c1f6351563d721137c22b9c8ccc48a2b14 100644 (file)
@@ -1,7 +1,6 @@
 [book]
 authors = ["Ben Pfaff"]
 language = "en"
-multilingual = false
 src = "src"
 title = "GNU PSPP"
 
@@ -9,6 +8,7 @@ title = "GNU PSPP"
 mathjax-support = true
 
 [output.linkcheck]
+optional = true
 
 [preprocessor.toc]
 command = "mdbook-toc"
index b35c29d723cf5698e2363824c637b99bd86501b6..64932bfc0e5088a12dff33a8ff5d1a7c63aef95a 100644 (file)
@@ -6,9 +6,9 @@
 - [Running PSPP](invoking/index.md)
   - [Converting Files](invoking/pspp-convert.md)
   - [Inspecting System Files](invoking/pspp-show.md)
-  - [Inspecting Portable Files](invoking/pspp-show-por.md)
-  - [Inspecting SPSS/PC+ Files](invoking/pspp-show-pc.md)
-  - [Inspecting SPSS Viewer Files](invoking/pspp-show-spv.md)
+  - [Inspecting Portable Files](invoking/por.md)
+  - [Inspecting SPSS/PC+ Files](invoking/pc.md)
+  - [Inspecting SPSS Viewer Files](invoking/spv.md)
   - [Identifying Files](invoking/pspp-identify.md)
   - [Decrypting Files](invoking/pspp-decrypt.md)
   - [Output Driver Configuration](invoking/output.md)
   - [Light Detail Members](spv/light-detail.md)
   - [Legacy Detail Binary Member](spv/legacy-detail-binary.md)
   - [Legacy Detail XML Member](spv/legacy-detail-xml.md)
+  - [Graphs](spv/graphs.md)
 - [SPSS TableLook File Formats](tablelook.md)
 - [Encrypted File Wrappers](encrypted-wrapper.md)
 - [SPSS Portable File Format](portable.md)
index 6585fc73e0ab1dd7719e83c3e0c37f04e41d4504..8941437f7abccee45a3109a34aaf73f086475619 100644 (file)
@@ -42,9 +42,14 @@ the names `ZSC000` through `ZSC999`, `STDZ00` through `STDZ09`,
 `ZZZZ00` through `ZZZZ09`, `ZQZQ00` through `ZQZQ09`, in that order.
 Z-score variable names may also be specified explicitly on `VARIABLES`
 in the variable list by enclosing them in parentheses after each
-variable.  When Z scores are calculated, PSPP ignores
-[`TEMPORARY`](temporary.md), treating temporary transformations as
-permanent.
+variable.
+
+With [`TEMPORARY`](temporary.md), Z scores are computed on the final
+values of the variables following the temporary transformations, but
+they are included in the permanent cases preceding
+[`TEMPORARY`](temporary.md).  When a temporary [`SELECT
+IF`](select-if.md) deletes a case or a temporary [`FILTER`](filter.md)
+filters it out, its Z score will be the system-missing value.
 
 The `STATISTICS` subcommand specifies the statistics to be displayed:
 
index ff1b65403eb353a5d369d53eb0c4c2f6b45cc3f1..dc0c7ec57fe6654b2ad081882f01818f13fa7454 100644 (file)
@@ -679,17 +679,21 @@ like the [elementwise operators](#elementwise-binary-operators).
   `CMAX(M)`  
   `CSUM(M)`  
   `CSSQ(M)`  
+  `MEAN(M)`  
      Returns a row vector with the same number of columns as `M`, in
-     which each element is the minimum, maximum, sum, or sum of
-     squares, respectively, of the elements in the same column of `M`.
+     which each element is the minimum, maximum, sum, sum of squares,
+     or mean, respectively, of the elements in the same column of `M`.
 
      ```
-     CMIN({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {1, 2, 3}
-     CMAX({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {7, 8, 9}
-     CSUM({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {12, 15, 18}
-     CSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {66, 93, 126}
+     CMIN({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {1, 2, 3}
+     CMAX({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {7, 8, 9}
+     CSUM({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {12, 15, 18}
+     CSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {66, 93, 126}
+     MEAN({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {4, 5, 6}
      ```
 
+     `MEAN` is a PSPP extension.
+
 * `MMIN(M)`  
   `MMAX(M)`  
   `MSUM(M)`  
@@ -698,10 +702,10 @@ like the [elementwise operators](#elementwise-binary-operators).
      of the elements of `M`.
 
      ```
-     MMIN({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ 1
-     MMAX({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ 9
-     MSUM({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ 45
-     MSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ 285
+     MMIN({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ 1
+     MMAX({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ 9
+     MSUM({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ 45
+     MSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ 285
      ```
 
 * `RMIN(M)`  
@@ -713,10 +717,10 @@ like the [elementwise operators](#elementwise-binary-operators).
      squares, respectively, of the elements in the same row of `M`.
 
      ```
-     RMIN({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {1; 4; 7}
-     RMAX({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {3; 6; 9}
-     RSUM({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {6; 15; 24}
-     RSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9} ⇒ {14; 77; 194}
+     RMIN({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {1; 4; 7}
+     RMAX({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {3; 6; 9}
+     RSUM({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {6; 15; 24}
+     RSSQ({1, 2, 3; 4, 5, 6; 7, 8, 9}) ⇒ {14; 77; 194}
      ```
 
 * `SSCP(M)`  
@@ -734,6 +738,21 @@ like the [elementwise operators](#elementwise-binary-operators).
      TRACE(MDIAG(1:5)) ⇒ 15
      ```
 
+* `COV(M)`  
+   Returns a symmetric matrix whose diagonal elements are the variance
+   of the each column in `M` and whose off-diagonal elements are the
+   covariance of the two columns of the element's row and column.  For
+   example, if `M` is a `N`×2 matrix, the result is a 2×2 matrix `{a,
+   b; b, c}`, where `a` is the variance of `M`'s first column, `b` is
+   the covariance between `M`'s two columns, and `c` is the variance
+   of `M`'s second column.
+
+   ```
+   COV({80, 70; 63, 20; 100, 50})} ⇒ {343, 260; 260, 633.33}
+   ```
+
+   `COV` is a PSPP extension.
+
 ### Matrix Property Functions
 
 * `NROW(M)`  
index bf0b0bb82c138f693f434986358bfbb779aa7d66..c125e930de0b071b748dd87a73753ccdcc353e93 100644 (file)
@@ -414,7 +414,7 @@ SET
 System file subcommands affect the default format of system files
 produced by PSPP.  These subcommands are
 
-* <a name="scompression">`SCOMPRESSION</a>`  
+* <a name="scompression">`SCOMPRESSION`</a>  
   Whether system files created by `SAVE` or `XSAVE` are compressed by
   default.  The default is `ON`.
 
index 3b8aaa4819252395dfc8020ceaeb08aa252ee59f..cd9cbb634c19262fdb5acd6c4731827e6b0e7c93 100644 (file)
@@ -47,6 +47,23 @@ This driver has the following options:
 
   [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
 
+* `images = "template-#.png"`  
+  `images = "none"`  
+  By default, when PSPP writes text output to a file named
+  `output.txt`, it writes graphs and images files named
+  `output-1.png`, `output-2.png`, and so on.  This option overrides
+  the names for these image files.  The option value should include
+  one `#`, which PSPP replaces by a sequential number.
+
+  When PSPP writes text output to stdout, then by default it does not
+  write graphs and images at all.  Specify this option to override
+  that.
+
+  Graph and image output is normally in PNG format.  If the option
+  value ends in `.svg`, then PSPP writes them in SVG format instead.
+
+  To disable graph and image output, specify `images = "none"`.
+
 # PDF Output (`.pdf`)
 
 This driver has the following options:
diff --git a/rust/doc/src/invoking/pc.md b/rust/doc/src/invoking/pc.md
new file mode 100644 (file)
index 0000000..baf2c4a
--- /dev/null
@@ -0,0 +1,66 @@
+# Inspecting SPSS/PC+ Files
+
+The `pspp pc` command reads an SPSS/PC+ system file which usually has
+a `.sys` extension, and produces a report.
+
+> SPSS/PC+ has been obsolete since the 1990s, and its file format is
+> also obsolete and rarely encountered.  Use [`pspp
+> show`](pspp-show.md) to inspect modern SPSS system files.
+
+The basic syntax is:
+
+```
+pspp pc <SUBCOMMAND> <INPUT> [OUTPUT]
+```
+
+where `<SUBCOMMAND>` is one of the subcommands detailed below,
+`<INPUT>` is the SPSS/PC+ file to read, and `[OUTPUT]` is the output
+file name.  If `[OUTPUT]` is omitted, output is written to the
+terminal.
+
+The following `<SUBCOMMAND>`s are available:
+
+* `dictionary`: Outputs the file dictionary in detail, including
+  variables, value labels, and so on.
+
+  This can be useful as an alternative to PSPP syntax commands such as
+  [`DISPLAY DICTIONARY`](../commands/display.md).
+
+  [`pspp convert`](pspp-convert.md) is a better way to convert an
+  SPSS/PC+ file to another format.
+
+  This subcommand has the following additional options:
+
+  - `--data [<MAX_CASES>]`  
+    This instructs `pspp pc` to read cases from the file.  If
+    `<MAX_CASES>` is given, then that sets a limit on the number of
+    cases to read.  Without this option, PSPP will not read any cases.
+
+* `metadata`: Outputs metadata not included in the dictionary:
+
+  - The creation date and time declared inside the file (not in the
+    file system).
+
+  - The name of the product family and product that wrote the file, if
+    present.
+
+  - The file name embedded inside the file, if one is present.
+
+  - Whether the file is bytecode-compressed.
+
+  - The number of cases in the file.
+
+## Options
+
+`pspp pc` accepts the following options:
+
+* `-O <OUTPUT_OPTIONS>`  
+  Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
+  [Output Drivers](output.md) for information on how to configure
+  output.
+
+  If no output driver is specified, the default output format is
+  chosen based on `[OUTPUT]`'s extension.  If `[OUTPUT]` is omitted,
+  output is written to stdout in [JSON](output.md#json-output-json)
+  format.
+
diff --git a/rust/doc/src/invoking/por.md b/rust/doc/src/invoking/por.md
new file mode 100644 (file)
index 0000000..9ffb649
--- /dev/null
@@ -0,0 +1,104 @@
+# Inspecting Portable (`.por`) Files with `pspp por`
+
+The `pspp por` command reads an SPSS "portable file", which usually
+has a `.por` extension, and produces a report.  The basic syntax is:
+
+```
+pspp por <SUBCOMMAND> <INPUT> [OUTPUT]
+```
+
+where `<SUBCOMMAND>` is a mode of operation (see below), `<INPUT>` is
+the SPSS portable file to read, and `[OUTPUT]` is the output file
+name.  If `[OUTPUT]` is omitted, output is written to the terminal.
+
+> The portable file format is mostly obsolete.  The "system file" or
+> `.sav` format should be used for writing new data files.  Use [`pspp
+> show`](pspp-show.md) to inspect `.sav` files.
+
+The following `<SUBCOMMAND>`s are available:
+
+* `dictionary`: Outputs the file dictionary in detail, including
+  variables, value labels, documents, and so on.
+
+  This can be useful as an alternative to PSPP syntax commands such as
+  [`DISPLAY DICTIONARY`](../commands/display.md).
+
+  [`pspp convert`](pspp-convert.md) is a better way to convert a
+  portable file to another format.
+
+  This subcommand accepts the following additional option:
+
+  - `--data [<MAX_CASES>]`  
+    This instructs `pspp por dictionary` to read cases from the file.
+    If `<MAX_CASES>` is given, then that sets a limit on the number of
+    cases to read.  Without this option, PSPP will not read any cases.
+
+* `metadata`: Outputs portable file metadata not included in the
+  dictionary:
+
+  - The creation date and time declared inside the file (not in the
+    file system).
+
+  - The name of the product and subproduct that wrote the file, if
+    present.
+
+  - The author of the file, if present.  This is usually the name of
+    the organization that licensed the product that wrote the file.
+
+  - The character set [translation table] embedded in the file, as an
+    array with 256 elements, one for each possible value of a byte in
+    the file.  Each array element gives the byte value as a 2-digit
+    hexadecimal number paired with the translation table's entry for
+    that byte.  Since the file can technically be in any encoding
+    (although [the corpus] universally uses extended ASCII), the entry
+    is given as a character interpreted in two character sets:
+    [windows-1252] and [code page 437], in that order.  (If the two
+    character sets agree on the code point, then it is only given
+    once.)
+
+    For example, consider a portable's file translation table at
+    offset 0x9e, which in the [portable character set] is `±`.
+    Suppose it has value 0xb1, which is `±` in [windows-1252] and `▒`
+    in [code page 437].  Then that array element would be `["9e", "±",
+    "▒"]`.
+
+    [translation table]: ../portable.md#translation-table
+    [the corpus]: ../portable.md#corpus
+    [portable character set]: ../portable.md#theory
+    [windows-1252]: https://en.wikipedia.org/wiki/Windows-1252
+    [code page 437]: https://en.wikipedia.org/wiki/Code_page_437
+
+  This command is most useful with some knowledge of the [portable
+  file format].
+
+  [portable file format]: ../portable.md
+
+* `histogram`: Reports on the usage of characters in the portable
+  file.  Produces output in the form of an array for each possible
+  value of a byte in the file.  Each array element gives the byte
+  value, the byte's character, and the number of times that the byte
+  appears in the file.  A given byte is omitted from the table if it
+  does not appear in the file at all, or if the translation table
+  leaves it unmapped.  It is also omitted if the byte's character is
+  the ISO-8859-1 encoding of the byte (for example, if byte 0x41
+  represents `A`, which is `A` in [ISO-8859-1]).
+
+  This command is most useful with some knowledge of the [portable
+  file format].
+
+  [ISO-8859-1]: https://en.wikipedia.org/wiki/ISO/IEC_8859-1
+
+## Options
+
+`pspp por` accepts the following options:
+
+* `-O <OUTPUT_OPTIONS>`  
+  Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
+  [Output Drivers](output.md) for information on how to configure
+  output.
+
+  If no output driver is specified, the default output format is
+  chosen based on `[OUTPUT]`'s extension.  If `[OUTPUT]` is omitted,
+  output is written to stdout in [JSON](output.md#json-output-json)
+  format.
+
index 950dc0bebeab2b3a3dd482515b81348cb39165cb..825a57cbb6dbae6a5b2e582bde0a4c3f4502ef51 100644 (file)
@@ -4,38 +4,54 @@ The `pspp convert` command reads a file and writes it to another,
 usually in a different format.  The basic syntax is:
 
 ```
-pspp convert <INPUT> [OUTPUT]
+pspp convert <INPUT>... [-o <OUTPUT>]
 ```
 
-which reads `<INPUT>` and writes a copy of it to `[OUTPUT]`, or to the
-terminal if `[OUTPUT]` is omitted.
+which reads each `<INPUT>` and writes a copy to `<OUTPUT>` if the `-o`
+option is specified, or to the terminal otherwise.
 
 `pspp convert` can convert following kinds of files:
 
 * Data files:
 
-  When `<INPUT>` is an SPSS system file or portable file or an SPSS/PC+
-  system file, then `[OUTPUT]` can be a data file format.  The
-  currently supported data file formats are:
+  When `<INPUT>` is a single SPSS system file or portable file or
+  SPSS/PC+ system file, the currently supported output file formats
+  are:
 
   - [Comma-separated value (CSV) files]
   - [SPSS system files]
   - [SPSS portable files]
 
-  If `[OUTPUT]` is omitted, the default is to write the data to stdout
-  in CSV format.
+  If `-o <OUTPUT>` is omitted, the default is to write the data to
+  stdout in CSV format.
 
 * Viewer files:
 
-  When `<INPUT>` is an SPSS viewer (SPV) file, then `[OUTPUT]` may be
-  any [PSPP output format], including:
+  When `<INPUT>` is one or more SPSS viewer (SPV) files, then the
+  output may be any [PSPP output format], including:
 
   - [plain text]
   - [PDF]
   - [HTML]
 
-  If `[OUTPUT]` is omitted, the default is to write the viewer file to
-  stdout in plain text format.
+  If `-o <OUTPUT>` is omitted, the default is to write the viewer file
+  to stdout in plain text format.
+
+  For viewer file input, [selection
+  options](#viewer-file-selection-options) can select a subset of the
+  viewer file for output.
+
+  If more than one viewer file is specified as input, they are
+  concatenated in the order specified to produce the output.
+
+* Chart XML files:
+
+  The SPSS graph editor can export charts as individual XML files.
+  PSPP can read and convert these to any of the support output
+  formats.
+
+  Any number of viewer files or chart XML files may be specified as
+  input.
 
 [Converting SPSS Viewer Files]: pspp-convert-spv.md
 [Comma-separated value (CSV) files]: output.md#comma-separated-value-output-csv
@@ -50,6 +66,11 @@ terminal if `[OUTPUT]` is omitted.
 
 `pspp convert` accepts the following options:
 
+* `-o <OUTPUT>`  
+  Sets the output file name.  The extension on `<OUTPUT>` determines
+  the default output format.  Without this option, PSPP writes output
+  to standard output.
+
 * `-e <ENCODING>`  
   `--encoding=<ENCODING>`  
   Sets the character encoding used to read text strings in the input
@@ -90,7 +111,7 @@ terminal if `[OUTPUT]` is omitted.
   > ⚠️ The password (and other command-line options) may be visible to
   > other users on multiuser systems.
 
-* `-o <OUTPUT_OPTIONS>`  
+* `-O <OUTPUT_OPTIONS>`  
   Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
   [Output Drivers](output.md) for information on how to configure
   output.
@@ -101,3 +122,88 @@ terminal if `[OUTPUT]` is omitted.
   a data file, or in [plain text] format if `<INPUT>` is an SPV file.
 
 [CSV]: output.md#comma-separated-value-output-csv
+
+## Viewer File Selection Options
+
+When `pspp convert` reads a viewer (SPV) file, by default it operates
+on all of the file's objects (text, tables, and so on), except for
+hidden objects (which are not shown in the output viewer window).  The
+following options can select a subset of the input objects.  When
+multiple options are used, only objects that satisfy all of them are
+selected.
+
+* `--select=[^]CLASS...`  
+  Include only objects of the given `CLASS`; with leading `^`, include
+  only objects not in the class.  Use commas to separate multiple
+  classes.  The supported classes are:
+
+  * `charts`
+  * `headings`
+  * `logs`
+  * `models`
+  * `tables`
+  * `texts`
+  * `trees`
+  * `warnings`
+  * `outlineheaders`
+  * `pagetitle`
+  * `notes`
+  * `unknown`
+  * `other`
+
+* `--commands=[^]COMMAND...`  
+  `--subtypes=[^]SUBTYPE...`  
+  `--labels=[^]LABEL...`  
+  Include only objects with the specified `COMMAND`, `SUBTYPE`, or
+  `LABEL`.  With a leading `^`, include only the objects that do not
+  match.  Multiple values may be specified separated by commas.  An
+  asterisk at the end of a value acts as a wildcard.
+
+  The `--command` option matches command identifiers, case
+  insensitively.  All of the objects produced by a single command use
+  the same, unique command identifier.  Command identifiers are
+  always in English regardless of the language used for output.  They
+  often differ from the command name in PSPP syntax.  Use the
+  `pspp-output` program's `dir` command to print command identifiers
+  in particular output.
+
+  The `--subtypes` option matches particular tables within a command,
+  case insensitively.  Subtypes are not necessarily unique: two
+  commands that produce similar output tables may use the same
+  subtype.  Only tables have subtypes, so specifying `--subtypes` will
+  exclude other kinds of objects.  Subtypes are always in English and
+  `dir` will print them.
+
+  The `--labels` option matches the labels in table output (that is,
+  the table titles).  Labels are affected by the output language,
+  variable names and labels, split file settings, and other factors.
+
+* `--nth-commands=N...`  
+  Include only objects from the `N`th command that matches `--command`
+  (or the `N`th command overall if `--command` is not specified),
+  where `N` is 1 for the first command, 2 for the second, and so on.
+
+* `--instances=INSTANCE...`  
+  Include the specified `INSTANCE` of an object that matches the other
+  criteria within a single command.  `INSTANCE` may be a number (1 for
+  the first instance, 2 for the second, and so on) or `last` for the
+  last instance.
+
+* `--show-hidden`  
+  Include hidden output objects in the output.  By default, they are
+  excluded.
+
+* `--or`  
+  Separates two sets of selection options.  Objects selected by
+  either set of options are included in the output.
+
+The following additional input selection options are intended mainly
+for use by PSPP developers:
+
+*  `--members=MEMBER...`  
+  Include only the objects that include a listed Zip file `MEMBER`.
+  More than one name may be included, comma-separated.  The members in
+  an SPV file may be listed with the `dir` command by adding the
+  `--member-names` option or with `zipinfo` or another program to view
+  Zip files.  Error messages that `pspp-output` prints when it reads
+  SPV files also often include member names.
diff --git a/rust/doc/src/invoking/pspp-show-pc.md b/rust/doc/src/invoking/pspp-show-pc.md
deleted file mode 100644 (file)
index a933ace..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-# Inspecting SPSS/PC+ Files
-
-The `pspp show-pc` command reads an SPSS/PC+ system file which
-usually has a `.sys` extension, and produces a report.
-
-> SPSS/PC+ has been obsolete since the 1990s, and its file format is
-> also obsolete and rarely encountered.  Use [`pspp
-> show`](pspp-show.md) to inspect modern SPSS system files.
-
-The basic syntax is:
-
-```
-pspp show-pc <MODE> <INPUT> [OUTPUT]
-```
-
-where `<MODE>` is a mode of operation (see below), `<INPUT>` is the
-SPSS/PC+ file to read, and `[OUTPUT]` is the output file name.  If
-`[OUTPUT]` is omitted, output is written to the terminal.
-
-The following `<MODE>`s are available:
-
-* `dictionary`: Outputs the file dictionary in detail, including
-  variables, value labels, and so on.  With `--data`, also outputs
-  cases from the system file.
-
-  This can be useful as an alternative to PSPP syntax commands such as
-  [`DISPLAY DICTIONARY`](../commands/display.md).
-
-  [`pspp convert`](pspp-convert.md) is a better way to convert an
-  SPSS/PC+ file to another format.
-
-* `metadata`: Outputs metadata not included in the dictionary:
-
-  - The creation date and time declared inside the file (not in the
-    file system).
-
-  - The name of the product family and product that wrote the file, if
-    present.
-
-  - The file name embedded inside the file, if one is present.
-
-  - Whether the file is bytecode-compressed.
-
-  - The number of cases in the file.
-
-## Options
-
-`pspp show-pc` accepts the following options:
-
-* `--data [<MAX_CASES>]`  
-  For mode `dictionary`, and `encodings`, this instructs `pspp
-  show-pc` to read cases from the file.  If `<MAX_CASES>` is given,
-  then that sets a limit on the number of cases to read.  Without this
-  option, PSPP will not read any cases.
-
-* `-o <OUTPUT_OPTIONS>`  
-  Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
-  [Output Drivers](output.md) for information on how to configure
-  output.
-
-  If no output driver is specified, the default output format is
-  chosen based on `[OUTPUT]`'s extension.  If `[OUTPUT]` is omitted,
-  output is written to stdout in [JSON](output.md#json-output-json)
-  format.
-
diff --git a/rust/doc/src/invoking/pspp-show-por.md b/rust/doc/src/invoking/pspp-show-por.md
deleted file mode 100644 (file)
index 83e0a3d..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-# Inspecting Portable (`.por`) Files with `pspp show-por`
-
-The `pspp show-por` command reads an SPSS "portable file",
-which usually has a `.por` extension, and produces a report.  The
-basic syntax is:
-
-```
-pspp show-por <MODE> <INPUT> [OUTPUT]
-```
-
-where `<MODE>` is a mode of operation (see below), `<INPUT>` is the
-SPSS portable file to read, and `[OUTPUT]` is the output file name.
-If `[OUTPUT]` is omitted, output is written to the terminal.
-
-> The portable file format is mostly obsolete.  The "system file" or
-> `.sav` format should be used for writing new data files.  Use [`pspp
-> show`](pspp-show.md) to inspect `.sav` files.
-
-The following `<MODE>`s are available:
-
-* `dictionary`: Outputs the file dictionary in detail, including
-  variables, value labels, documents, and so on.  With `--data`, also
-  outputs cases from the system file.
-
-  This can be useful as an alternative to PSPP syntax commands such as
-  [`DISPLAY DICTIONARY`](../commands/display.md).
-
-  [`pspp convert`](pspp-convert.md) is a better way to convert a
-  portable file to another format.
-
-* `metadata`: Outputs portable file metadata not included in the
-  dictionary:
-
-  - The creation date and time declared inside the file (not in the
-    file system).
-
-  - The name of the product and subproduct that wrote the file, if
-    present.
-
-  - The author of the file, if present.  This is usually the name of
-    the organization that licensed the product that wrote the file.
-
-  - The character set [translation table] embedded in the file, as an
-    array with 256 elements, one for each possible value of a byte in
-    the file.  Each array element gives the byte value as a 2-digit
-    hexadecimal number paired with the translation table's entry for
-    that byte.  Since the file can technically be in any encoding
-    (although [the corpus] universally uses extended ASCII), the entry
-    is given as a character interpreted in two character sets:
-    [windows-1252] and [code page 437], in that order.  (If the two
-    character sets agree on the code point, then it is only given
-    once.)
-
-    For example, consider a portable's file translation table at
-    offset 0x9e, which in the [portable character set] is `±`.
-    Suppose it has value 0xb1, which is `±` in [windows-1252] and `▒`
-    in [code page 437].  Then that array element would be `["9e", "±",
-    "▒"]`.
-
-    [translation table]: ../portable.md#translation-table
-    [the corpus]: ../portable.md#corpus
-    [portable character set]: ../portable.md#theory
-    [windows-1252]: https://en.wikipedia.org/wiki/Windows-1252
-    [code page 437]: https://en.wikipedia.org/wiki/Code_page_437
-
-  This command is most useful with some knowledge of the [portable
-  file format].
-
-  [portable file format]: ../portable.md
-
-* `histogram`: Reports on the usage of characters in the portable
-  file.  Produces output in the form of an array for each possible
-  value of a byte in the file.  Each array element gives the byte
-  value, the byte's character, and the number of times that the byte
-  appears in the file.  A given byte is omitted from the table if it
-  does not appear in the file at all, or if the translation table
-  leaves it unmapped.  It is also omitted if the byte's character is
-  the ISO-8859-1 encoding of the byte (for example, if byte 0x41
-  represents `A`, which is `A` in [ISO-8859-1]).
-
-  This command is most useful with some knowledge of the [portable
-  file format].
-
-  [ISO-8859-1]: https://en.wikipedia.org/wiki/ISO/IEC_8859-1
-
-## Options
-
-`pspp show-por` accepts the following options:
-
-* `--data [<MAX_CASES>]`  
-  For mode `dictionary`, and `encodings`, this instructs `pspp
-  show-por` to read cases from the file.  If `<MAX_CASES>` is given,
-  then that sets a limit on the number of cases to read.  Without this
-  option, PSPP will not read any cases.
-
-* `-o <OUTPUT_OPTIONS>`  
-  Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
-  [Output Drivers](output.md) for information on how to configure
-  output.
-
-  If no output driver is specified, the default output format is
-  chosen based on `[OUTPUT]`'s extension.  If `[OUTPUT]` is omitted,
-  output is written to stdout in [JSON](output.md#json-output-json)
-  format.
-
diff --git a/rust/doc/src/invoking/pspp-show-spv.md b/rust/doc/src/invoking/pspp-show-spv.md
deleted file mode 100644 (file)
index e080f10..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-# Inspecting SPSS Viewer Files
-
-The `pspp show-spv` command reads SPSS Viewer (SPV) files, whose names
-usually have an `.spv` extension, and produces a report.  The basic
-syntax is:
-
-```
-pspp show-spv <MODE> <INPUT> [OUTPUT]
-```
-
-where `<MODE>` is a mode of operation (see below) and `<INPUT>` is the
-SPV file to read, and `[OUTPUT]` is the output file name.  If
-`[OUTPUT]` is omitted, output is written to the terminal.
-
-The following `<MODE>`s are accepted:
-
-* `dir`: Outputs a table of contents for the SPV file, listing every
-  selected object, which by default is every object except for hidden
-  ones.
-
-  The following additional option for `dir` is intended mainly for use
-  by PSPP developers:
-
-  - `--member-names`: Also show the names of the ZIP file members
-    associated with each object.
-
-* `get-table-look`: Extracts the TableLook from the first table in the
-  selected objects and outputs it in TableLook XML format.  The output
-  file should have an `.stt` extension.
-
-  Use `-` for `<INPUT>` to instead write the default TableLook.
-
-* `convert-table-look`: Reads an `.stt` or `.tlo` TableLook file as
-  `<INPUT>` and outputs it in TableLook XML format.  The output file
-  should have an `.stt` extension.
-
-  This is useful for converting a TableLook `.tlo` file from SPSS 15
-  or earlier into the newer `.stt` format.
-
-## Options
-
-These options apply to any `<MODE>` that reads an SPV file:
-
-* `-p <PASSWORD>`  
-  `--password=<PASSWORD>`  
-  Specifies the password for reading an encrypted SPV file.
-
-  PSPP reads, but does not write, encrypted SPV files.
-
-  > ⚠️ The password (and other command-line options) may be visible to
-  other users on multiuser systems.
-
-## Input Selection Options
-
-Commands that read an SPV file operate, by default, on all of the
-objects in the file, except for objects that are not visible in the
-output viewer window.  The user may specify these options to select a
-subset of the input objects.  When multiple options are used, only
-objects that satisfy all of them are selected:
-
-* `--select=[^]CLASS...`  
-  Include only objects of the given `CLASS`; with leading `^`, include
-  only objects not in the class.  Use commas to separate multiple
-  classes.  The supported classes are:
-
-  * `charts`
-  * `headings`
-  * `logs`
-  * `models`
-  * `tables`
-  * `texts`
-  * `trees`
-  * `warnings`
-  * `outlineheaders`
-  * `pagetitle`
-  * `notes`
-  * `unknown`
-  * `other`
-
-* `--commands=[^]COMMAND...`  
-  `--subtypes=[^]SUBTYPE...`  
-  `--labels=[^]LABEL...`  
-  Include only objects with the specified `COMMAND`, `SUBTYPE`, or
-  `LABEL`.  With a leading `^`, include only the objects that do not
-  match.  Multiple values may be specified separated by commas.  An
-  asterisk at the end of a value acts as a wildcard.
-
-  The `--command` option matches command identifiers, case
-  insensitively.  All of the objects produced by a single command use
-  the same, unique command identifier.  Command identifiers are
-  always in English regardless of the language used for output.  They
-  often differ from the command name in PSPP syntax.  Use the
-  `pspp-output` program's `dir` command to print command identifiers
-  in particular output.
-
-  The `--subtypes` option matches particular tables within a command,
-  case insensitively.  Subtypes are not necessarily unique: two
-  commands that produce similar output tables may use the same
-  subtype.  Only tables have subtypes, so specifying `--subtypes` will
-  exclude other kinds of objects.  Subtypes are always in English and
-  `dir` will print them.
-
-  The `--labels` option matches the labels in table output (that is,
-  the table titles).  Labels are affected by the output language,
-  variable names and labels, split file settings, and other factors.
-
-* `--nth-commands=N...`  
-  Include only objects from the `N`th command that matches `--command`
-  (or the `N`th command overall if `--command` is not specified),
-  where `N` is 1 for the first command, 2 for the second, and so on.
-
-* `--instances=INSTANCE...`  
-  Include the specified `INSTANCE` of an object that matches the other
-  criteria within a single command.  `INSTANCE` may be a number (1 for
-  the first instance, 2 for the second, and so on) or `last` for the
-  last instance.
-
-* `--show-hidden`  
-  Include hidden output objects in the output.  By default, they are
-  excluded.
-
-* `--or`  
-  Separates two sets of selection options.  Objects selected by
-  either set of options are included in the output.
-
-The following additional input selection options are intended mainly
-for use by PSPP developers:
-
-* `--errors`  
-  Include only objects that cause an error when read.  With the
-  `convert` command, this is most useful in conjunction with the
-  `--force` option.
-
-*  `--members=MEMBER...`  
-  Include only the objects that include a listed Zip file `MEMBER`.
-  More than one name may be included, comma-separated.  The members in
-  an SPV file may be listed with the `dir` command by adding the
-  `--member-names` option or with `zipinfo` or another program to view
-  Zip files.  Error messages that `pspp-output` prints when it reads
-  SPV files also often include member names.
index db8c3fa5eff5aa201ea444c1ccf5be255eec315a..588837c46de938358fca71274509c9ddadd385a4 100644 (file)
@@ -81,7 +81,7 @@ The following `<MODE>`s are available:
   that sets a limit on the number of cases to read.  Without this
   option, PSPP will not read any cases.
 
-* `-o <OUTPUT_OPTIONS>`  
+* `-O <OUTPUT_OPTIONS>`  
   Adds `<OUTPUT_OPTIONS>` to the output engine configuration.  See
   [Output Drivers](output.md) for information on how to configure
   output.
diff --git a/rust/doc/src/invoking/spv.md b/rust/doc/src/invoking/spv.md
new file mode 100644 (file)
index 0000000..0ad312f
--- /dev/null
@@ -0,0 +1,61 @@
+# Inspecting SPSS Viewer Files
+
+The `pspp spv` command reads SPSS Viewer (SPV) files, whose names
+usually have an `.spv` extension, and produces a report.  The basic
+syntax is:
+
+```
+pspp spv <SUBCOMMAND> <INPUT> [OUTPUT]
+```
+
+where `<SUBCOMMAND>` is a subcommand (see below) and `<INPUT>` is the
+SPV file to read.  Some subcommands require `[OUTPUT]` as an output
+file name.
+
+The following `<SUBCOMMAND>`s are accepted:
+
+* `directory` or `dir`: Outputs a table of contents for the SPV file,
+  listing every selected object, which by default is every object
+  except for hidden ones.
+
+  The following option applies only to `dir`.  It is intended mainly
+  for use by PSPP developers:
+
+  - `--member-names`  
+    Show the names of the ZIP file members associated with each object.
+
+* `get-table-look`: Extracts the TableLook from the first table in the
+  selected objects and outputs it in TableLook XML format.  The output
+  file should have a `.stt` extension.
+
+  Use `-` for `<INPUT>` to instead write the default TableLook.
+
+* `convert-table-look`: Reads an `.stt` or `.tlo` TableLook file as
+  `<INPUT>` and outputs it in TableLook XML format.  The output file
+  should have a `.stt` extension.
+
+  This is useful for converting a TableLook `.tlo` file from SPSS 15
+  or earlier into the newer `.stt` format.
+
+* `export-charts`: Reads the SPV and writes out a standalone XML file
+  for each chart among the selected objects.  The required output file
+  name should have a `.xml` extension.  If more than one chart is
+  selected, then `-2`, `-3`, and so is inserted into the output file
+  name before its extension.
+
+## Options
+
+These options apply to any `<MODE>` that reads an SPV file:
+
+* `-p <PASSWORD>`  
+  `--password=<PASSWORD>`  
+  Specifies the password for reading an encrypted SPV file.
+
+  PSPP reads, but does not write, encrypted SPV files.
+
+  > ⚠️ The password (and other command-line options) may be visible to
+  other users on multiuser systems.
+
+The [Viewer File Selection
+Options](pspp-convert.md#viewer-file-selection-options) may also be
+used.
index a63e0a45cf17551b3c02bb7ab13c659b489906b8..cca3f3d35c00390553fa5e6a4c877699beb30041 100644 (file)
@@ -9,11 +9,13 @@ values.  Details of missing value exclusion depend on the procedure and
 can often be controlled by the user; refer to descriptions of individual
 procedures for details.
 
-   The system-missing value exists only for numeric variables.  String
+The system-missing value exists only for numeric variables.  String
 variables always have a defined value, even if it is only a string of
 spaces.
 
-   Variables, whether numeric or string, can have designated
-"user-missing values".  Every user-missing value is an actual value for
-that variable.  However, most of the time user-missing values are
-treated in the same way as the system-missing value.
+Variables, whether numeric or string, can have designated
+"user-missing values".  Every user-missing value is an actual value
+for that variable.  Any variables can have up to three user-missing
+values; numeric variables can also have a range of missing values.
+Most of the time, user-missing values are treated in the same way as
+the system-missing value.
diff --git a/rust/doc/src/spv/graphs.md b/rust/doc/src/spv/graphs.md
new file mode 100644 (file)
index 0000000..dcb2580
--- /dev/null
@@ -0,0 +1,996 @@
+# Graphs
+
+Graphs are expressed in the same "visualization XML" or "VizML" format
+as legacy tables, as described [previously](legacy-detail-xml.md).
+This section describes how the format applies to graphs.
+
+## The `visualization` Element
+
+```
+visualization
+   :creator
+   :date
+   :lang
+   :name
+   :style[style_ref]=ref style
+   :type
+   :version
+   :schemaLocation?
+=> visualization_extension?
+   userSource
+   (sourceVariable | derivedVariable)+
+   (categoricalDomain | intervalDomain)*
+   graph+
+   (labelFrame | container)*
+   style+
+   layerController?
+
+extension[visualization_extension]
+   :numRows=int?
+   :showGridline=bool?
+   :minWidthSet=(true)?
+   :maxWidthSet=(true)?
+=> EMPTY
+
+userSource :missing=(listwise | pairwise)? => EMPTY
+
+categoricalDomain => variableReference simpleSort
+
+simpleSort :method[sort_method]=(custom) => categoryOrder
+
+categoryOrder => TEXT
+
+container :style=ref style => container_extension? location* labelFrame*
+
+extension[container_extension] :combinedFootnotes=(true) => EMPTY
+
+layerController
+   :source=(tableData)
+   :target=ref label?
+=> EMPTY
+```
+
+## The `graph` element
+
+```
+graph
+    :id
+    :axesOutside=bool?
+    :cellStyle=ref style?
+    :style=ref style?
+    :clip=bool?
+=> location* coordinates* faceting? facetLayout?
+   (area | interval | line | edge | path | point | polygon | schema)*
+   (frameGuide | functionGuide | lineGuide | rectangleGuide | planeGuide | pointGuide)*
+```
+
+```
+userSource :id
+delimitedFileSource :id :fileName :missing=(pairwise)? :encoding?
+
+embeddedSource :id => names types row*
+names => TEXT
+types => TEXT
+row => TEXT
+```
+
+Both `userSource` and `embeddedSource` refer to the `dataPath` in the
+structure XML.  `delimitedFileSource` refers to the `csvPath`.
+
+The `encoding` in `delimitedFileSource`, if present, is always `UTF-8`.
+
+`embeddedSource` contains the data inline in the XML.  This is
+produced by "Export Chart XML" in the GUI.  The `id` is mandatory,
+used by `sourceVariable` to refer to this data source.  Each of the
+`names`, `types`, and `row` elements inside `embeddedSource` consists
+of a collection of semicolon-delimited fields, with the same number of
+fields inside all the elemnents in a given `embeddedSource`.
+Semicolons within a field are backslash-escaped.  These elements are
+used as follows:
+
+* `names`: Variable names.
+
+* `types`: Either `string` or `double`, the type of each variable.
+
+* `row`: Data in each row.
+
+If there is more than one `coordinates`:
+
+- Most commonly, generally only one of them is referenced and the
+  other one is:
+
+  ```
+      <coordinates id="coordinates1">
+        <dimension lowerMargin="5%" upperMargin="5%"/>
+        <dimension lowerMargin="15%" upperMargin="5%"/>
+      </coordinates>
+  ```
+
+- Other times, the second `coordinates` is referenced but it is
+  degenerate, like this:
+
+  ```
+      <coordinates id="coordinates"/>
+  ```
+
+- The case of
+  `d578c36ad2b0865328de224657b3421b/00000000668112__chart.xml` shows
+  that there can be three `coordinates`, two of which are
+  non-degenerate, and all referenced.
+
+
+## Variables
+
+
+```
+sourceVariable
+   :id
+   :source
+   :sourceName
+   :categorical=bool?
+   :creator?
+   :key=bool?
+   :domain=ref (categoricalDomain | intervalDomain)?
+   :label=ref (derivedVariable | sourceVariable)?
+   :labelVariable=ref sourceVariable?
+   :inputDateFormatPattern?
+   :measurementLevel=(nominal|ratio)?
+   :shortLabel?
+   :domainModifier=ref domainModifier?
+   :weight=bool?
+   :description=(labeling)?
+=> variable_extension* (format | stringFormat)?
+```
+
+`source` refers to the `id` of a `userSource`, `delimitedSource`, or `embeddedSource`.
+
+`inputDateFormatPattern` values:
+* `HH:mm:ss.SSS`
+* `yyyy-MM-dd`
+* `yyyy-MM-dd'T'HH:mm:ss.SSS`
+
+
+```
+derivedVariable
+   :id
+   :categorical=bool?
+   :value
+   :creator?
+   :key=(false)?
+   :domain=ref (categoricalDomain | intervalDomain)?
+   :label=ref (derivedVariable | sourceVariable)?
+   :weight=(false)?
+=> variable_extension* (format | stringFormat)? valueMapEntry*
+```
+
+`value` takes one of the following forms:
+
+* `constant(0)`  
+  `constant(VARIABLE)`  
+  All zeros.  The reason why a variable is sometimes named is
+  unknown.  Sometimes the "variable name" has spaces in it.
+
+* `map(VARIABLE)`  
+  Transforms the values in the named VARIABLE using the
+  `valueMapEntry`s contained within the element.
+
+* `VARIABLE<RELOP>?IFTRUE:IFFALSE`  
+  where:
+
+  - `RELOP` is one of `<>`, `!=`, `>=`, `<=`, `==`, `=`, `<`, or `>`
+
+  - `OPERAND`, `IFTRUE`, and `IFFALSE` are each a number, a variable
+    name, or a string quoted with `"` or `'`.
+
+  Examples:
+
+  - `x>=0?"0positive":"1negative"`
+  - `xVariable<100?"a":"b"`
+
+* `a+b+c+...`  
+  where each of the operands is either a variable name or a `"`-quoted
+  string.  The variables and quoted strings get concatenated in some
+  nonobvious way.
+
+
+```
+expressionVariable
+    :id
+    :expression
+    :categorical=bool
+```
+
+`expression` is a simple expression. Only `+` and `/` operators seen
+in the corpus.  The only constant seen in the corpus is 1.  The
+expression `x+x` for some variable `x` is seen often.  It looks like
+these expressions are Grammar of Graphics expressions where `+` is
+"dot" or "blend" and `/` is "nest".  Variable names are not always
+ASCII, e.g. the corpus contains `Пол+Пол` (Russian for
+`Gender+Gender`).  Some expressions are very long (e.g. the corpus
+contains one that has over 100 `+` and `/` operators).
+
+Forms of value:
+* `abs(weight)`
+* `Category`
+* `constant(foo)`
+* `label(variableName)`
+* `"quoted string"`
+* `rowIndex()`
+
+## `categoricalDomain`
+
+```
+categoricalDomain
+    :date=bool?
+=> (variableReference | domainReference | categories | categoryOrder | simpleSort | statisticSort)*
+
+domainReference :ref=ref categoricalDomain => EMPTY
+variableReference :ref=ref (sourceVariable | derivedVariable) => EMPTY
+categories :remove=bool? => TEXT
+
+simpleSort
+    :id
+    :method=(custom|data|natural|naturalUsingLabel)?
+    :descending=bool?
+=> sortMove? categoryOrder?
+categoryOrder => TEXT
+statisticSort
+    :id
+    :descending=bool?
+    :method=(mean|sum)?
+    :sourceVariable=ref sourceVariable?
+    :statVariable=ref sourceVariable?
+=> sortMove*
+sortMove :id :first=bool? :category => EMPTY
+```
+
+The `categories` (and `categoryOrder`) text is semicolon-separated
+numbers or strings.  Examples:
+
+```
+<categories remove="false" />
+<categories remove="false">0;1;2;3</categories>
+<categories remove="false">1.00;2.00;3.00</categories>
+<categories remove="false">ApparelNone;Cap;LongSleeve;Necklace;Scrunchie;Sweater;TShirt;Tank;Toque</categories>
+<categories remove="false">f;m</categories>
+<categories remove="false">AT;BE;CH;CZ;DE;DK;EE;ES;FI;FR;GB;HU;IE;IL;LT;NL;NO;PL;PT;SE;SI</categories>
+```
+
+`remove="true"` is uncommon but it exists:
+
+```
+<categories remove="true">1;4;7;8;9;12;13;14;15;16;17</categories>
+<categories remove="true">MD-x</categories>
+```
+
+Sometimes there are multiple `<categories>` in a single
+`<categoricalDomain>` where the first adds categories and the second
+removes some of them:
+
+```
+<categoricalDomain id="categoricalDomain_115">
+    <variableReference ref="ExpGroup"/>
+    <categories remove="false">0.00;1.00;2.00;3.00;4.00;5.00;6.00</categories>
+    <categories remove="true">3.00;4.00</categories>
+    <simpleSort descending="false" id="simpleSort_1055224" method="natural"/>
+</categoricalDomain>
+```
+
+Sometimes there are really bizarrely many:
+
+```
+<categoricalDomain id="categoricalDomain_1096877">
+    <categories remove="false"/>
+    <variableReference ref="活动"/>
+    <categories remove="false"/>
+    <categories remove="true">采集桃胶;贩卖;观景;集会;麻将;书法;听广播;挖土;玩手机;喂鱼;洗衣服;宣传;阅读;织毛衣;遛鸟;</categories>
+    <simpleSort id="simpleSort_1132087" method="custom">
+        <categoryOrder>看护;锻炼;散步;聊天;音乐;跳舞;闲坐;垂钓;放风筝;穿行;文娱活动;拍照;观演;遛狗;停留;棋牌;进食</categoryOrder>
+    </simpleSort>
+</categoricalDomain>
+```
+
+`domainReference` seems to always refer to another `categoricalDomain`
+that the referrer copies.  Suppose `A` refers to `B`.  The corpus
+shows duplicate `domainReferences`: `A` references `B` twice The
+corpus shows that `A` also includes the same `variableReference` as
+`B` being referenced.  In at least one case, `A` also references one
+more variable than `B` did.  In the corpus, `B` has no further uses,
+it's only referred to by `A`.
+
+Example:
+
+```
+  <categoricalDomain id="categoricalDomain_1019">
+    <domainReference ref="categoricalDomain_1004"/>
+    <variableReference ref="context"/>
+    <domainReference ref="categoricalDomain_1004"/>
+  </categoricalDomain>
+  <categoricalDomain id="categoricalDomain_1004">
+    <categories remove="false">.00;1.00</categories>
+    <variableReference ref="context"/>
+    <simpleSort id="simpleSort_1011" method="natural"/>
+  </categoricalDomain>
+```
+
+```
+  <categoricalDomain id="categoricalDomain_1828">
+    <domainReference ref="categoricalDomain_1794"/>
+    <variableReference ref="lineStyle"/>
+    <domainReference ref="categoricalDomain_1794"/>
+    <variableReference ref="nodeColor"/>
+  </categoricalDomain>
+  <categoricalDomain id="categoricalDomain_1794">
+    <variableReference ref="lineStyle"/>
+    <simpleSort id="simpleSort_1799" method="natural"/>
+  </categoricalDomain>
+```
+
+## `intervalDomain`
+
+```
+intervalDomain :date=bool? => (range | format | variableReference)*
+range :min=float? :max=float? :exact=bool? => EMPTY
+```
+
+The `date` attribute is only observed with value `false`.
+
+An `intervalDomain` contains at least one `range` or
+`variableReference` and can contain both.
+
+There can be multiple `variableReference`s but usually there is just one.
+
+Most commonly, there is at most one `range` with both `min` and `max`,
+but:
+
+- Sometimes only `min` or `max` is given.
+- Sometimes there are multiple `range`s (one for `min`, one for `max`).
+- Sometimes no `min` or `max` is given at all despite the `range`.
+- If there is more than one `range`, `exact` is always the same on
+  both.
+- If `min` or `max` is present on multiple `range`s, they're given the
+  same value each time.
+- If `min` and `max` have equal values, `exact` is false.
+
+If `exact` is true then no `variableReference`s are present.
+
+```
+  <intervalDomain date="false" id="domain5">
+    <range exact="false" max="0" min="0"/>
+    <variableReference ref="variable6"/>
+  </intervalDomain>
+  <intervalDomain id="domain5">
+    <variableReference ref="variable7"/>
+    <variableReference ref="variable17"/>
+  </intervalDomain>
+  <intervalDomain date="false" id="domain7">
+    <range exact="false" max="0" min="0"/>
+  </intervalDomain>
+  <intervalDomain date="false" id="domain5">
+    <range exact="false" max="0" min="0"/>
+    <range exact="false" max="0" min="0"/>
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_1301020">
+    <range exact="true" max="1" min="0"/>
+  </intervalDomain>
+  <intervalDomain date="false" id="intervalDomain_230">
+    <range exact="true" min="120.0"/>
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_1027">
+    <range exact="true" min="120"/>
+    <range exact="true" max="140"/>
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_1083">
+    <range exact="true"/>
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_1022453">
+    <format hourFormat="AS_24" maximumFractionDigits="0" minimumFractionDigits="0" minimumIntegerDigits="1" separatorChars=".,/:" yearAbbreviation="false"/>
+    <range max="2" min="-0.106228"/>
+  </intervalDomain>
+```
+
+## Coordinates
+
+```
+coordinates :id
+=> (dimension
+    | insetTransform
+    | obliqueTransform
+    | parallelCoordinatesTransform
+    | polarTransform
+    | rectangularTransform
+    | reflectionTransform
+    | rotationTransform
+    | sameRatioTransform
+    | transposeTransform)*
+```
+
+There are usually two `dimension`s, but there can be only one.  There
+can be three if there is a transform from 3 dimensions down to 2 (such
+as `rectangularTransform`).  The dimensions are in the order X, Y, Z.
+
+There is usually at most one transform, but sometimes more.
+
+```
+dimension
+    :id
+    :domain=ref domain?
+    :lowerMargin=percentage?
+    :upperMargin=percentage?
+    :clusterGap=percentage?
+    :clusterVariable=ref variable?
+    :unionDomain=bool?
+    :niceUpperMapping=bool?
+    :niceLowerMapping=bool?
+    :facetsShareScale=bool?
+=> axis{0,2}
+```
+
+Presumably a dimension can have two axes if one is on either side.
+
+The `lowerMargin` and `upperMargin` are either `0`, or `5%` (or
+another percentage), or `20px` (or another number of pixels), or,
+weirdly and in just one case, `५%`.
+
+```
+axis
+    :id
+    :combineAxisLabels=bool?
+    :repeat=(never)?
+    :style=ref style?
+    :maxSize=dimension?
+    :opposite=bool?
+=> label? majorTicks? minorTicks? axisTransformParameter*
+
+majorTicks
+    :id?
+    :style=ref style?
+    :markStyle=ref style?
+    :tickFrameStyle=ref style?
+    :stagger=(false)?
+    :base=real?
+    :labelAngle=real?
+    :labelFrequency=real?
+    :position=(outside|cross)?
+    :length=dimension?
+    :delta=real?
+=> EMPTY
+
+minorTicks
+    :id?
+    :markStyle=ref style
+    :number=integer
+    :position=(outside|inside|cross)?
+=> EMPTY
+
+axisTransformParameter
+    :id
+    :parameterName=(base-ratio|derived-ratio|derived-match)
+    :parameterValue=real
+=> EMPTY
+```
+
+`majorTicks` requests drawing the axis itself and tick marks and
+labels.  When the axis is faceted, the axis can either be drawn just
+once or separately in each cell, according to the rules below, in
+order of decreasing priority:
+
+- If the dimension has a polar transform, then the axis is drawn in
+  every cell (because there's no reasonable way to draw it just once).
+
+- If the `axis` specifies `repeat="never"`, then the axis is drawn
+  just once.  (This can produce deceptive results if `faceting`
+  specifies `method="nest"`, since in that case different facet cells
+  might have different axes.)
+
+- If `faceting` specifies `method="nest"`, then the axis is drawn in
+  every cell because each cell has a different axis.
+
+- If the `axis` specifies `repeat="always"`, then the axis is drawn in
+  every cell.
+
+- Otherwise, the axis is drawn just once.
+
+`base`, if present, is most commonly zero but can be another number.
+
+`labelAngle`, if present, is -90, 90, 0, 45, 270, or 310 in the corpus.
+
+`labelFrequency` is only present as 1.
+
+`delta` is most commonly 1 or 10.
+
+`length` is only present as `0px` or `20px`.
+
+`number` is usually `2` but also present as `5` or `3`.
+
+## Element elements
+
+```
+interval :centerIn3D=bool?
+| (area|line|path)
+    :breakRepresentation=(gap|interpolate|wings)?
+    :showDiscontinuity=bool?
+    :closed=bool?
+    :preserveStraightLines=bool?
+| (edge|polygon) :preserveStraightLines=bool?
+| schema
+    :medianStyle=ref style?
+    :fenceStyle=ref style?
+    :hingeStyle=ref style?
+    :outlierStyle=ref style?
+    :extremeStyle=ref style?
+=> (position | x | y | z | color)+
+
+x :id :variable=ref (derivedVariable | sourceVariable) => EMPTY
+y :id :variable=ref (derivedVariable | sourceVariable) => EMPTY
+z :id :variable=ref (derivedVariable | sourceVariable) => EMPTY
+position :id :variable=ref (derivedVariable | sourceVariable) => EMPTY
+```
+
+Each of these elements represents a graphical element such as a bar
+chart (`interval`), a line graph (`line`), and so on.  They share many
+of their attributes and content; the above description takes some
+liberties with our XML description syntax to avoid redundancy.
+
+An `interval` element is always a conventional bar chart with vertical
+bars.  (Horizontal bars are achieved using a `transposeTransform`.)
+
+## Frames
+
+The `visualization`, `container`, `labelFrame`, `legend`, and `graph`
+elements are frames.  `visualization` and `container` can contain any
+frame types except for `visualization`, nested arbitrarily, and the
+other types do not have children:
+
+```
+<frame>
+    :id
+    :style=ref style?
+    :clip=bool?
+=> location*
+
+location
+    :id
+    :part=(left|right|top|bottom|width|height)
+    :method=(fixed|sizeToContent|same|attach)
+    :value
+    :min?
+    :max?
+    :target=ref <frame>?
+    :specifiedByUser=bool?
+=> EMPTY
+```
+
+Each frame occupies a rectangular space, specified using `location`
+elements:
+
+- `part` is the side of the rectangle, or its dimension, that
+  `location` specifies.
+
+- `method` is how the side or dimension is specified:
+
+  * `fixed`: As specified by `value`.
+
+  * `sizeToContent`: For `width` and `height` parts only, this says to
+    calculate the "natural size" of the content, constrained by the
+    size along the opposite axis if it is specified on a separate
+    `location` element.
+
+    The natural size of `graph` is 400px wide by 300px tall, plus
+    axes.  This is true even if the graph doesn't contain any elements
+    (like `line`).  The natural size along one dimension isn't
+    affected by any constrains on the other.
+
+    The natural size of `labelFrame` is the space needed to render its
+    text.  If the width is constrained, this affects the natural
+    height.
+
+  * `same`: The `target` must be the `id` of another frame.  The other
+    frame's value for the same part is used.
+
+  * `attach`: For `left`, `right`, `top`, and `bottom` parts only, the
+    `target` must be the `id` of another frame.  The other frame's
+    value for the opposite side.
+
+- `value`: For method `fixed` only, a length as a number followed by
+  an optional unit (`%`, `px`, `in`, `cm`, `mm`, `pt`, `pc`).  If
+  there is no unit, it is implicitly `px`.
+
+  For `left`, `right`, `top`, and `bottom` parts, `value` is an offset
+  from the top or left side of the parent rectangle.  For `width` and
+  `height` parts, it is a length.  Percentages are in terms of the
+  size of the parent frame.
+
+  > `cm` and `pc` don't appear in the corpus.
+  >
+  > The schema says that a set of three semicolon-separated lengths
+  > may be used, instead of just one, but this never occurs in the
+  > corpus.
+
+- `min`: For methods `fixed` and `sizeToContent` only, and for parts
+  `width` and `height` only, the minimum value for the width or
+  height, using the same syntax and interpretation as `value`.
+
+  > `min` only appears in the corpus with `px` units.
+
+- `max`: Same as `min` but for the maximum value.
+
+  > `max` only appears in the corpus with `%` and `px` units.
+
+Many `location`s are implicit, so that a constraint solver is needed
+to resolve frame locations:
+
+- `left` and `top` default to 0px if not constrained.
+
+- If a frame's width or height is unconstrained, as well as its
+  position, it occupies the full span of its parent frame along that
+  axis.
+
+- If a frame's width or height is contrained, but not its position, it
+  is centered along that axis in the parent frame.
+
+- If nothing else determines the size of a frame, it assigns natural
+  sizes to its children.
+
+Typical frames and constraints, with extraneous details removed:
+
+```
+<visualization>
+  <location method="fixed" part="left" value="0px" />
+  <location method="fixed" part="right" value="625px" />
+  <location method="fixed" part="top" value="0px" />
+  <location method="fixed" part="bottom" value="500px" />
+  <graph cellStyle="facetLabel_data_1s" id="frame_data_1" style="frame_data_1s">
+    <location method="fixed" part="left" value="1%" />
+    <location method="attach" part="top" target="frame_title_1" />
+    <location method="fixed" part="bottom" value="99%" />
+    <location method="fixed" part="right" value="99%" />
+  </graph>
+  <labelFrame id="frame_title_1" style="frame_title_1s">
+    <location method="fixed" part="top" value="1%" />
+    <location method="sizeToContent" part="height" />
+  </labelFrame>
+</visualization>
+```
+
+```
+<visualization>
+  <graph id="graph_2916">
+    <location method="attach" part="right" target="container_2928" />
+    <location method="attach" part="top" target="labelFrame_2942" />
+  </graph>
+  <container clip="false" id="container_2928">
+    <location method="same" part="top" target="graph_2916" />
+    <location method="same" part="bottom" target="graph_2916" />
+    <location max="25%" method="sizeToContent" part="width" />
+    <location method="fixed" part="right" value="100%" />
+    <legend tickTextStyle="style_2956">
+      <location method="sizeToContent" part="width" />
+      <location method="sizeToContent" part="height" />
+      <location method="fixed" part="left" value="0px" />
+      <location method="attach" part="top" target="labelFrame_2942" />
+    </legend>
+  </container>
+  <labelFrame id="labelFrame_2942" style="style_2947">
+    <location method="fixed" part="height" value="56px" />
+    <location method="fixed" part="top" value="2.5%" />
+    <location method="fixed" part="left" value="2%" />
+    <location method="fixed" part="right" value="98%" />
+  </labelFrame>
+</visualization>
+```
+
+Containers can be assumed to be just big enough to hold their
+children, if they are underspecified, such as for `visualization` in
+the second example.
+
+`visualization` can be assumed to start at the origin.
+
+Offsets are relative to their parent containers, for example above the
+`value="0px"` inside the `legend` element is relative to the left side
+of the container above it, not to the `visualization`.
+
+The size of the visualization as a whole is set through `right` (or
+`width`) and `bottom` (or `height`) on `visualization`.  It isn't
+available from the structure XML.
+
+Default width and height seems to be the same as `sizeToContent`?  The
+default width and height of an element without axes appears to be
+300px wide by 225px high, or 400x300 (which is also a 4:3 ratio) in
+another view.  The same element shows in print preview as about 362px
+wide in "as is" mode.
+
+## `faceting`
+
+The following variations on `faceting` have been observed in the corpus:
+
+```
+    <faceting id="faceting_20076" method="dot">
+      <dot id="dot_20077">
+        <variableReference ref="Panel"/>
+      </dot>
+    </faceting>
+
+    <faceting id="faceting_185" method="nest">
+      <cross id="cross_186">
+        <unity id="unity_187"/>
+        <variableReference ref="variable4"/>
+      </cross>
+    </faceting>
+
+    <faceting id="faceting_49" method="nest">
+      <cross id="cross_50">
+        <variableReference ref="variable4"/>
+        <variableReference ref="variable5"/>
+      </cross>
+    </faceting>
+
+    <faceting id="faceting0" method="nest">
+      <cross>
+        <unity/>
+        <nest>
+          <variableReference ref="sourceVariable2"/>
+        </nest>
+      </cross>
+    </faceting>
+
+    <faceting method="cross">
+      <cross>
+        <cross>
+          <variableReference ref="sourceVariable2"/>
+        </cross>
+      </cross>
+    </faceting>
+
+    <faceting id="faceting_849" method="cross">
+      <cross id="cross_862">
+        <unity id="unity_863"/>
+        <cross id="cross_850">
+          <variableReference ref="variable16"/>
+          <unity id="unity_852"/>
+          <variableReference ref="variable15"/>
+          <unity id="unity_854"/>
+          <variableReference ref="variable14"/>
+          <unity id="unity_856"/>
+          <variableReference ref="variable13"/>
+          <unity id="unity_858"/>
+          <variableReference ref="variable12"/>
+          <unity id="unity_860"/>
+          <variableReference ref="variable11"/>
+        </cross>
+      </cross>
+    </faceting>
+
+    <faceting id="faceting_1050807">
+      <cross id="cross_1050808">
+        <nest id="nest_1050809">
+          <variableReference ref="column"/>
+        </nest>
+        <nest id="nest_1050811">
+          <variableReference ref="row"/>
+        </nest>
+      </cross>
+    </faceting>
+
+    <faceting id="faceting_4486" method="cross">
+      <variableReference ref="variable11"/>
+    </faceting>
+
+    <faceting id="faceting_205" method="cross">
+      <nest id="nest_206">
+        <variableReference ref="variable6"/>
+        <nest id="nest_208">
+          <variableReference ref="variable5"/>
+          <variableReference ref="variable4"/>
+        </nest>
+      </nest>
+    </faceting>
+
+    <faceting id="faceting_2367" method="cross">
+      <cross id="cross_2368">
+        <variableReference ref="variable6"/>
+        <unity id="unity_2370"/>
+        <variableReference ref="variable5"/>
+        <unity id="unity_2372"/>
+        <variableReference ref="variable4"/>
+        <nest id="nest_2374">
+          <variableReference ref="variable8"/>
+          <variableReference ref="variable7"/>
+        </nest>
+      </cross>
+    </faceting>
+```
+
+The possible `method`s are `cross`, `dot`, and `nest`.  `dot` means
+the same as `blend`.  All the methods appear in the corpus.  The
+methods are about combining the "main expression" with the faceting
+(but I'm not sure what that means).  Observed behavior:
+
+* `cross`: The facets are all forced to have the same domain and axes.
+  This is the common case for faceted data.
+
+* `nest`: The facets can have different domains and axes according to
+  whatever data is in them.  For example, this is used when the facets
+  contain different pie charts, where stacking of percentages in
+  different facets produces, say, a total of 55% in one facet and 45%
+  in another, but both pie charts should be out of 100%.
+
+In the corpus, `faceting` only has a single child.  The schema says
+that it can have statistics children, but these never appear in the
+corpus.
+
+The `cross` element makes all combinations of the variables inside it.
+Its first, third, fifth, seventh, and other odd children are on the
+horizontal axis, and its second, fourth, sixth, eighth, and other even
+children are on the vertical axis.  The first children are the
+innermost.  `<unity/>` children may be used fillers in case there are
+no more variables on one axis or another.
+
+> Few `cross`es with more than one non-`unity` child appear in the
+> corpus, perhaps because it tends to cause crashes.
+
+A `blend` element is defined but never used in the corpus.
+
+The `dot` element usually has only one `variableReference` child, or
+one `variableReference` and one `unity` child.  In a few cases in the
+corpus, it has two `variableReference` children.
+
+The `nest` element usually has only one `variableReference` child.  In
+a few cases in the corpus, it has two `variableReference` children, or
+one `variableReference` child and an inner `nest` element with two
+`variableReference` children.
+
+Faceting can include layers but the corpus does not have any examples.
+
+"Graphboard editor"
+
+# Statistics
+
+distributionStatistics for normal distribution:
+
+* Calculates the mean and standard deviation of its variable
+
+* Displays a normal curve with those parameters.
+
+* Multiplies the normal curve by `n` times `binWidth`, where
+  `binWidth` is the bin width of a `binStatistics` for the same
+  variable, if there is one (even in a different element), or 1 if
+  there is no such `binStatistics`.
+
+## `format`
+
+`baseFormat` is required for dates and times and elapsed times.  If it
+is absent, the format is consider to be numeric.
+
+`minimumIntegerDigits` and `maximumIntegerDigits` bounds the number of
+integer digits that will be included:
+
+- If the number has fewer digits than the minimum number, it is padded
+  on the left with `0`s.
+
+- If the number needs more integer digits than the maximum, then the
+  number is displayed as an error.
+
+- At least one integer digit is displayed, unless the integer part of
+  the number is zero, and `minimumIntegerDigits` is also zero,
+  e.g. `.123`.
+
+`minimumFractionDigits` and `maximumFractionDigits` bound the number
+of digits in the fractional part:
+
+- Trailing zeros beyond `minimumFractionDigits` are dropped.
+
+If `minimumIntegerDigits` and `minimumFractionDigits` are both zero,
+and the value rounds to 0, then the number will display as `0`
+(instead of disappearing).
+
+| Number | minI | maxI | minF | maxF |   Result |
+|-------:|-----:|-----:|-----:|-----:|---------:|
+|    123 | none | none | none | none |    `123` |
+|    123 |    4 | none | none | none |   `0123` |
+|    123 | none |    2 | none | none |    error |
+|    123 | none | none |    1 | none |  `123.0` |
+|  123.4 | none | none | none |    1 |  `123.4` |
+|  123.4 | none | none | none |    2 |  `123.4` |
+|  123.4 | none | none |    2 | none | `123.40` |
+|  0.123 | none | none |    0 | none |  `0.123` |
+|  0.123 |    0 | none |    0 | none |   `.123` |
+|  0.123 |    0 | none | none |    1 |     `.1` |
+|  0.123 |    0 | none | none |    0 |      `0` |
+|   0.01 |    0 | none | none |    0 |      `0` |
+
+# patterns
+
+0. solid
+1. mesh
+2. diagonal
+3. vertical
+4. grid
+5. mesh2
+6. mesh3
+7. pluses
+8. diagonal2
+9. horizontal
+10. grid2
+11. grid3
+12. checkered
+13. mesh4
+14. diagonal3
+15. vertical2
+16. grid4
+17. diagonal4
+18. diagonal5
+19. mesh5
+20. pluses2
+21. diagonal6
+22. horizontal2
+23. grid5
+24. diagonal7
+
+pattern1 written as 12
+pattern2 written as 2
+pattern3 written as 3
+pattern4 written as 4
+pattern5 written as 5
+pattern6 written as 1
+pattern7 written as 7
+pattern8 written as 8
+pattern9 written as 9
+pattern10 written as 10
+pattern11 written as 11
+pattern12 written as 6
+pattern13 written as 13
+pattern14 written as 14
+pattern15 written as 27
+pattern16 written as 16
+pattern17 written as 17
+pattern18 written as 18
+pattern19 written as 19
+pattern20 written as 20
+pattern21 written as 15
+pattern22 written as 22
+pattern23 written as 23
+pattern24 written as 24
+pattern25 written as 25
+pattern26 written as 26
+pattern27 written as 21
+pattern28 written as 28
+pattern29 written as 29
+
+checkered => 12
+stripes like \ => 2
+stripes like / => 8
+stripes like | => 3
+stripes like - => 9
+
+
+Formatting of axis labels comes from the variable specified on an
+element placed on that axis.  If there is more than one such variable,
+it appears that the variable with the name alphabetically earlier is
+used.  Whether a given variable has a specified format is not
+considered.  The details are muddled; for example, it appears that if
+one of the variables lacks a specified format entirely, then none of
+their formats is used.
+
+
+Nice column and row facets found by searching for 2-facetLevel xmls:
+/home/blp/pspp/spv/spv/04488e072882e8f8b0c3c687e272d4af/00000000293_-4000816061397075370_chart.xml
+
+Different contents for different cells:
+/home/blp/pspp/spv/spv/271f44f7ba0037acfe63ef8e3e7c8918/0000000003312_-6910783111487815678_chart.xml
+
+Two dotted (not crossed) facet levels (this is the only example):
+/home/blp/pspp/spv/spv/cfe40eafe2c4122adbb5412f3d463bcd/00000000302_7579179872145113090_chart.xml
+
+Rotated matrix chart with multiple facetLevels:
+/home/blp/pspp/spv/spv/301cd2376ccc9f5db2fe0df3aa91eac0/00000000013__chart.xml
+
+Rotated weird text chart with multiple facetLevels:
+/home/blp/pspp/spv/spv/66c4eb85507e7b950ff06a0aad36a466/00000002282__chart.xml
+
+Normal-ish barchart with multiple facetLevels:
+/home/blp/pspp/spv/spv/9538e0d28ff95b21336ea19a551affca/00000000072_2564001948504162306_chart.xml
+
+# To-Do
+
+* Weighting with `weight="true"`
index e3945ceb886d59326839d615f3174991916624c0..2d625944901f070cb35e7017f7be0b2d9a2191da 100644 (file)
@@ -682,7 +682,7 @@ course, but it is usefully indicative.
 
 Each `facetLevel` contains an `axis`, which in turn may contain a
 [`label`](#the-label-element) for the `facetLevel` and does contain a
-`majorTicks` element.
+`majorTicks` element with the following attributes:
 
 * `labelAngle`  
   Normally 0.  The value -90 causes inner column or outer row labels
index cba0fb94542e1c16d6bc1cacb52ccefb3bbc4e9d..136ed56636f502c01dac47e9a8eac01bac2af137 100644 (file)
@@ -120,6 +120,9 @@ syntax:
 * `B+`  
   One or more instances of A.
 
+* `A{N,M}`
+  At least `N` and at most `M` instances of `A`.
+
 * `(SUBEXPRESSION)`  
   Grouping for a subexpression.
 
@@ -493,7 +496,7 @@ This element contains the following:
 
 ```
 graph
-   :VDPId?
+   :VDPId=int
    :ViZmlSource?
    :commandName?
    :creator-version?
@@ -519,6 +522,11 @@ The named Zip members are CSV text files (despite the `.bin` extension).
 The CSV files are encoded in UTF-8 and begin with a U+FEFF byte-order
 marker.
 
+`VDPId` must be present.  SPSS writes a large positive or negative
+number.  PSPP just writes 0.
+
+A writer may omit the other attributes.
+
 ## The `model` Element
 
 ```
index 026e7cdb73be5e1c82259fb25cdfb97f3bf00257..0b314582d80965e70f179620ff5a62cd87699c07 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -16,8 +16,8 @@
 
 use proc_macro::TokenStream;
 use proc_macro2::{Literal, TokenStream as TokenStream2};
-use quote::{format_ident, quote, ToTokens};
-use syn::{spanned::Spanned, Attribute, DataEnum, DataStruct, DeriveInput, Error, Fields, Token};
+use quote::{ToTokens, format_ident, quote};
+use syn::{Attribute, DataEnum, DataStruct, DeriveInput, Error, Fields, Token, spanned::Spanned};
 
 #[proc_macro_derive(FromTokens, attributes(pspp))]
 pub fn from_tokens_derive(input: TokenStream) -> TokenStream {
index d6f0499da51693788fe1cf41b4a982645ac1108d..a5697ead2411e9ac26e0df443c7f9f5f69924612 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -17,7 +17,7 @@
 use std::collections::HashMap;
 
 use tokio::sync::Mutex;
-use tower_lsp::{jsonrpc::Result, lsp_types::*, Client, LanguageServer, LspService, Server};
+use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc::Result, lsp_types::*};
 
 #[tokio::main]
 async fn main() {
index d218d63714dea84ed3d8efd674a3b07d2eef5e58..c26629b906a3ad99fbac842498ffe0dc2fbe97b2 100644 (file)
@@ -1 +1,2 @@
+/cldr
 /cldr-json-full.zip
index d764e472c668b84bc2a258c3872ee801781b6766..daafe95045f498bacaa5ba4b6bbd9212ec1da762 100644 (file)
@@ -7,66 +7,75 @@ description = "Statistical analysis software"
 license = "GPL-3.0-or-later"
 
 [dependencies]
+aes = "0.8.4"
 anyhow = "1.0.69"
+base64 = "0.22.1"
+binrw = "0.15.2"
+bit-vec = "0.8.0"
+cairo-rs = { version = "0.22.0", features = ["ps", "png", "pdf", "svg"] }
+chardetng = "0.1.17"
+chrono = { version = "0.4.40", features = ["serde"] }
 clap = { version = "4.1.7", features = ["derive", "wrap_help"] }
+cmac = "0.7.2"
+codepage-437 = "0.1.0"
+color = { version = "0.2.3", features = ["serde"] }
+csv = "1.3.1"
+derive_more = { version = "2.0.1", features = ["debug"] }
+displaydoc = "0.2.5"
+either = "1.13.0"
 encoding_rs = { version = "0.8.32", features = ["serde"] }
+enum-iterator = "2.1.0"
+enum-map = { version = "3.1.0", features = ["serde"] }
+enumset = "1.1.10"
+erased-serde = "0.4.9"
 flate2 = "1.0.26"
+gio = "0.22.2"
+hashbrown = { version = "0.15.5", features = ["serde"] }
 hexplay = "0.2.1"
+html_parser = "0.7.0"
+indexmap = { version = "2.13.0", features = ["serde"] }
+itertools = "0.14.0"
+libc = "0.2.147"
+libm = "0.2.11"
+librsvg = "2.62.0"
+ndarray = "0.16.1"
+nodit = "0.10.0"
 num = "0.4.0"
 ordered-float = "3.7.0"
-thiserror = "1.0"
-chrono = { version = "0.4.40", features = ["serde"] }
-unicase = "2.6.0"
-libc = "0.2.147"
-indexmap = { version = "2.1.0", features = ["serde"] }
-unicode-width = "0.2.0"
-chardetng = "0.1.17"
-enum-map = { version = "2.7.3", features = ["serde"] }
+pango = "0.22.0"
+pangocairo = "0.22.0"
+paper-sizes = { path = "../paper-sizes", version = "0.4.0", features = ["serde"] }
 pspp-derive = { version = "0.1.0", path = "../pspp-derive" }
-either = "1.13.0"
-enum-iterator = "2.1.0"
-smallvec = { version = "1.13.2", features = ["const_generics", "write"] }
-libm = "0.2.11"
-smallstr = "0.3.0"
-itertools = "0.14.0"
-unicode-linebreak = "0.1.5"
 quick-xml = { version = "0.38.4", features = ["serialize", "escape-html"] }
-serde = { version = "1.0.218", features = ["derive", "rc"] }
-color = { version = "0.2.3", features = ["serde"] }
-binrw = "0.14.1"
-ndarray = "0.16.1"
-derive_more = { version = "2.0.1", features = ["debug"] }
-cairo-rs = { version = "0.20.7", features = ["ps", "png", "pdf", "svg"] }
-pango = "0.20.9"
-pangocairo = "0.20.7"
-zip = "4.0.0"
-csv = "1.3.1"
-cmac = "0.7.2"
-aes = "0.8.4"
 readpass = "1.0.3"
-zeroize = "1.8.1"
-unicode-properties = "0.1.3"
-unicode-segmentation = "1.12.0"
+rs-stats = "2.0.0"
+serde = { version = "1.0.218", features = ["derive", "rc"] }
 serde_json = "1.0.141"
-toml = "0.9.5"
-hashbrown = { version = "0.15.5", features = ["serde"] }
-displaydoc = "0.2.5"
-codepage-437 = "0.1.0"
 serde_path_to_error = "0.1.20"
-html_parser = "0.7.0"
-paper-sizes = { path = "../paper-sizes", version = "0.4.0", features = ["serde"] }
-enumset = "1.1.10"
-bit-vec = "0.8.0"
-erased-serde = "0.4.9"
-nodit = "0.10.0"
+smallstr = "0.3.0"
+smallvec = { version = "1.13.2", features = ["const_generics", "write"] }
+svg = "0.18.0"
+thiserror = "1.0"
+toml = "0.9.5"
+unicase = "2.6.0"
+unicode-linebreak = "0.1.5"
+unicode-properties = "0.1.3"
+unicode-segmentation = "1.12.0"
+unicode-width = "0.2.0"
+zeroize = "1.8.1"
+zip = "4.0.0"
 
 [target.'cfg(windows)'.dependencies]
 windows-sys = { version = "0.48.0", features = ["Win32_Globalization"] }
 
 [build-dependencies]
 anyhow = "1.0.69"
-zip = "4.0.0"
+enum-map = { version = "2.7.3", features = ["serde"] }
+serde = { version = "1.0.218", features = ["derive"] }
 serde_json = "1.0.141"
+quick-xml = { version = "0.38.4" }
+
+zip = "4.0.0"
 
 [[bin]]
 name = "pspp"
@@ -76,5 +85,7 @@ path = "src/main.rs"
 path = "src/lib.rs"
 
 [dev-dependencies]
+approx = "0.5.1"
 diff = "0.1.13"
 rand = "0.9.0"
+similar = "3.1.0"
index 31f478c009c6f493c783e51395c52ad982a12e61..558c9fd8e85c371d1cd43aa6af202beb8bf97baa 100644 (file)
@@ -14,7 +14,7 @@ This new version is very incomplete--in fact, only a subset of the
 
 To install this version of PSPP from source, first install Rust using
 the instructions at https://www.rust-lang.org/tools/install.  PSPP
-requires Rust 1.88 or later.  Once you've done that, you can download
+requires Rust 1.98 or later.  Once you've done that, you can download
 PSPP source code from `crates.io` and build and install it with:
 
 ```
index fc849245b8a2aaaece23942062248ddaf13532fb..545489cc2b05a4cbbed759898bc61a2d938b91b1 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
 use anyhow::{Result as AnyResult, anyhow};
-use serde_json::Value;
+use enum_map::{Enum, EnumMap};
+use quick_xml::events::Event;
+use serde::{Deserialize, Deserializer};
+use serde_json::{Value, from_value};
 use std::{
-    collections::{BTreeMap, HashSet, VecDeque},
+    collections::{BTreeMap, HashMap, HashSet, VecDeque},
     env::var_os,
     ffi::OsStr,
-    fmt::Write as _,
+    fmt::{Display, Write as _},
     fs::{File, read_to_string},
     io::{Error as IoError, Write},
     path::{Path, PathBuf},
@@ -75,22 +78,22 @@ fn process_converter<'a>(
             continue;
         }
 
-        if let Some(number) = name.strip_prefix("cp") {
-            if let Ok(number) = number.parse::<CodepageNumber>() {
-                cps.insert(Source::Codepage, number);
-            }
+        if let Some(number) = name.strip_prefix("cp")
+            && let Ok(number) = number.parse::<CodepageNumber>()
+        {
+            cps.insert(Source::Codepage, number);
         }
 
-        if let Some(number) = name.strip_prefix("windows-") {
-            if let Ok(number) = number.parse::<CodepageNumber>() {
-                cps.insert(Source::Windows, number);
-            }
+        if let Some(number) = name.strip_prefix("windows-")
+            && let Ok(number) = number.parse::<CodepageNumber>()
+        {
+            cps.insert(Source::Windows, number);
         }
 
-        if let Some(number) = name.strip_prefix("ibm-") {
-            if let Ok(number) = number.parse::<CodepageNumber>() {
-                cps.insert(Source::Ibm, number);
-            }
+        if let Some(number) = name.strip_prefix("ibm-")
+            && let Ok(number) = number.parse::<CodepageNumber>()
+        {
+            cps.insert(Source::Ibm, number);
         }
     }
 
@@ -213,17 +216,412 @@ fn build_encodings() -> AnyResult<()> {
     Ok(())
 }
 
-/// Read `cldr-json-full.zip` and write `src/format/decimals.rs`.
-fn builld_decimals() -> AnyResult<()> {
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Calendar {
+    months: Months,
+    days: Days,
+    day_periods: DayPeriods,
+    eras: Eras,
+    date_formats: DateFormats,
+    time_formats: DateFormats,
+    date_time_formats: DateFormats,
+}
+
+#[derive(Debug)]
+struct DateFormats(EnumMap<Length, String>);
+
+impl Display for DateFormats {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "        {full},
+        {long},
+        {medium},
+        {short},",
+            full = Quoted(&self.0[Length::Full]),
+            long = Quoted(&self.0[Length::Long]),
+            medium = Quoted(&self.0[Length::Medium]),
+            short = Quoted(&self.0[Length::Short]),
+        )
+    }
+}
+
+impl<'de> Deserialize<'de> for DateFormats {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Debug, Deserialize)]
+        #[serde(untagged)]
+        enum Pattern {
+            String(String),
+            Map {
+                #[serde(rename = "_value")]
+                value: String,
+                _numbers: String,
+            },
+        }
+        let source_map = HashMap::<String, Value>::deserialize(deserializer)?;
+        let mut map = EnumMap::default();
+        for (key, value) in source_map {
+            let length = match key.as_str() {
+                "full" => Length::Full,
+                "long" => Length::Long,
+                "medium" => Length::Medium,
+                "short" => Length::Short,
+                _ => continue,
+            };
+            let value = match from_value::<Pattern>(value).unwrap() {
+                Pattern::String(s) => s,
+                Pattern::Map { value, .. } => value,
+            };
+            map[length] = value.into();
+        }
+        Ok(Self(map))
+    }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct DayPeriods {
+    format: DayPeriodsInner,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct DayPeriodsInner {
+    narrow: DayPeriodNames,
+    abbreviated: DayPeriodNames,
+    wide: DayPeriodNames,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Months {
+    format: MonthsInner,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MonthsInner {
+    abbreviated: MonthNames,
+    narrow: MonthNames,
+    wide: MonthNames,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct MonthNames {
+    #[serde(rename = "1")]
+    jan: String,
+    #[serde(rename = "2")]
+    feb: String,
+    #[serde(rename = "3")]
+    mar: String,
+    #[serde(rename = "4")]
+    apr: String,
+    #[serde(rename = "5")]
+    may: String,
+    #[serde(rename = "6")]
+    jun: String,
+    #[serde(rename = "7")]
+    jul: String,
+    #[serde(rename = "8")]
+    aug: String,
+    #[serde(rename = "9")]
+    sep: String,
+    #[serde(rename = "10")]
+    oct: String,
+    #[serde(rename = "11")]
+    nov: String,
+    #[serde(rename = "12")]
+    dec: String,
+}
+
+impl MonthNames {
+    fn as_array(&self) -> [&str; 12] {
+        [
+            self.jan.as_str(),
+            self.feb.as_str(),
+            self.mar.as_str(),
+            self.apr.as_str(),
+            self.may.as_str(),
+            self.jun.as_str(),
+            self.jul.as_str(),
+            self.aug.as_str(),
+            self.sep.as_str(),
+            self.oct.as_str(),
+            self.nov.as_str(),
+            self.dec.as_str(),
+        ]
+    }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Days {
+    format: DaysInner,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct DaysInner {
+    narrow: DayNames,
+    abbreviated: DayNames,
+    short: DayNames,
+    wide: DayNames,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct DayNames {
+    sun: String,
+    mon: String,
+    tue: String,
+    wed: String,
+    thu: String,
+    fri: String,
+    sat: String,
+}
+
+impl DayNames {
+    fn as_array(&self) -> [&str; 7] {
+        [
+            self.sun.as_str(),
+            self.mon.as_str(),
+            self.tue.as_str(),
+            self.wed.as_str(),
+            self.thu.as_str(),
+            self.fri.as_str(),
+            self.sat.as_str(),
+        ]
+    }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct DayPeriodNames {
+    am: String,
+    pm: String,
+}
+
+#[derive(Copy, Clone, Debug, Deserialize, Enum)]
+#[serde(rename_all = "camelCase")]
+enum Length {
+    Full,
+    Long,
+    Medium,
+    Short,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Eras {
+    era_abbr: EraNames,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct EraNames {
+    #[serde(rename = "0")]
+    bce: String,
+    #[serde(rename = "1")]
+    ce: String,
+}
+
+struct Quoted<'a>(&'a str);
+
+impl<'a> Display for Quoted<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_char('"')?;
+        for c in self.0.chars() {
+            match c {
+                '"' | '\\' => f.write_char('\'')?,
+                _ => (),
+            }
+            f.write_char(c)?;
+        }
+        f.write_char('"')
+    }
+}
+
+struct QuotedChar(char);
+
+impl Display for QuotedChar {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_char('\'')?;
+        if self.0 == '\\' || self.0 == '\'' {
+            f.write_char('\\')?;
+        }
+        f.write_char(self.0)?;
+        f.write_char('\'')
+    }
+}
+
+fn write_locales(
+    numbers: &BTreeMap<String, NumberSymbols>,
+    calendars: &BTreeMap<String, Calendar>,
+) -> AnyResult<()> {
+    let mut contents = String::new();
+    writeln!(
+        &mut contents,
+        "//! Autogenerated locale data for all of the CLDR locales.
+//!
+//! The code in this file is generated by `build.rs`.  It is unconventional
+//! to check generated files into a repository, but it is done in this case
+//! because the source data is large and the generated code rarely needs to
+//! change.
+//!
+//! To regenerate this code, download a CLDR JSON release from
+//! <https://cldr.unicode.org/index/downloads>, rename it as
+//! `cldr-json-full.zip` in the same directory as `build.rs`,
+//! and touch `build.rs` to force a rebuild.
+//!
+//! The CLDR is covered by the Unicode License v3, available at
+//! <https://github.com/unicode-org/cldr/blob/main/LICENSE>.
+
+use std::{{collections::HashMap, sync::LazyLock}};
+use crate::locale::Locale;"
+    )?;
+
+    fn lang_to_var_name(lang: &str) -> String {
+        lang.replace('-', "_").to_ascii_uppercase()
+    }
+
+    for (lang, calendar) in calendars {
+        writeln!(
+            &mut contents,
+            "/// Data for `{lang}` locale.
+pub static {lang_var}: Locale<'static> = Locale {{
+    lang: {quoted_lang},
+    dates: Locale::new_patterns(
+{date}
+    ),
+    times: Locale::new_patterns(
+{time}
+    ),
+    date_times: Locale::new_patterns(
+{date_time}
+    ),
+    months: Locale::new_months(",
+            lang_var = lang_to_var_name(lang),
+            quoted_lang = Quoted(lang),
+            date = &calendar.date_formats,
+            time = &calendar.time_formats,
+            date_time = &calendar.date_time_formats
+        )?;
+        for months in [
+            &calendar.months.format.wide,
+            &calendar.months.format.abbreviated,
+            &calendar.months.format.narrow,
+        ] {
+            writeln!(&mut contents, "        [")?;
+            for month in months.as_array() {
+                writeln!(&mut contents, "         {},", Quoted(month))?;
+            }
+            writeln!(&mut contents, "        ],")?;
+        }
+        writeln!(
+            &mut contents,
+            "    ),
+    days: Locale::new_days("
+        )?;
+        for days in [
+            &calendar.days.format.wide,
+            &calendar.days.format.abbreviated,
+            &calendar.days.format.short,
+            &calendar.days.format.narrow,
+        ] {
+            writeln!(&mut contents, "        [")?;
+            for day in days.as_array() {
+                writeln!(&mut contents, "         {},", Quoted(day))?;
+            }
+            writeln!(&mut contents, "        ],")?;
+        }
+        writeln!(
+            &mut contents,
+            "    ),
+    day_periods: Locale::new_day_periods("
+        )?;
+        for day_period_names in [
+            &calendar.day_periods.format.wide,
+            &calendar.day_periods.format.abbreviated,
+            &calendar.day_periods.format.narrow,
+        ] {
+            writeln!(
+                &mut contents,
+                "        [{}, {}],",
+                Quoted(day_period_names.am.as_str()),
+                Quoted(day_period_names.pm.as_str()),
+            )?;
+        }
+        let symbols = numbers.get(lang.as_str());
+        writeln!(
+            &mut contents,
+            "    ),
+    eras: Locale::new_eras({bce}, {ce}),
+    decimal: {decimal},
+    group: {group},
+}};",
+            bce = Quoted(calendar.eras.era_abbr.bce.as_str()),
+            ce = Quoted(calendar.eras.era_abbr.ce.as_str()),
+            decimal = QuotedChar(symbols.map_or('.', |symbols| symbols.decimal)),
+            group = QuotedChar(symbols.map_or(',', |symbols| symbols.group)),
+        )?;
+    }
+
+    writeln!(
+        &mut contents,
+        "
+
+/// Map from language to locale date patterns.
+pub static LANG_TO_LOCALE:
+    LazyLock<HashMap<&'static str, &'static Locale<'static>>> = LazyLock::new(|| {{
+    ["
+    )?;
+
+    for lang in calendars.keys() {
+        writeln!(
+            &mut contents,
+            "     ({}, &{}),",
+            Quoted(lang.as_str()),
+            lang_to_var_name(lang.as_str())
+        )?;
+    }
+
+    write!(
+        &mut contents,
+        "    ].into_iter().collect()
+}});"
+    )?;
+
+    let output_file_name = Path::new("src/locale/data.rs");
+    if !output_file_name.try_exists()? || contents.as_bytes() != std::fs::read(output_file_name)? {
+        std::fs::write(output_file_name, &contents)?;
+    }
+    Ok(())
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct NumberSymbols {
+    decimal: char,
+    group: char,
+}
+
+/// Read `cldr-json-full.zip` and write `src/locale/data.rs`.
+fn build_locales() -> AnyResult<()> {
     let cldr_path = Path::new("cldr-json-full.zip");
     if !cldr_path.try_exists()? {
-        // Don't try to regenerate `decimals.rs`, since we don't have the CLDR.
+        // Don't try to regenerate the files, since we don't have the CLDR.
         return Ok(());
     }
     println!("cargo:rerun-if-changed={}", cldr_path.display());
 
     let mut archive = ZipArchive::new(File::open(cldr_path)?)?;
-    let mut decimals = BTreeMap::new();
+    let mut numbers = BTreeMap::new();
+    let mut calendars = BTreeMap::new();
     for i in 0..archive.len() {
         let name = Path::new(archive.name_for_index(i).unwrap());
         if name.file_name() == Some(OsStr::new("numbers.json")) {
@@ -232,71 +630,225 @@ fn builld_decimals() -> AnyResult<()> {
             if let Some(main) = json.get("main")
                 && let Some(langs) = main.as_object()
                 && let Some((lang, details)) = langs.iter().next()
-                && let Some(numbers) = details.get("numbers")
-                && let Some(latin) = numbers.get("symbols-numberSystem-latn")
-                && let Some(decimal) = latin.get("decimal")
-                && let Some(decimal) = decimal.as_str()
-                && let Some(decimal) = decimal.chars().next()
-                && (decimal == '.' || decimal == ',')
+                && let Some(latin) = details.pointer("/numbers/symbols-numberSystem-latn")
+                && let Ok(symbols) = from_value::<NumberSymbols>(latin.clone())
+            {
+                numbers.insert(lang.clone(), symbols);
+            }
+        } else if name.file_name() == Some(OsStr::new("ca-gregorian.json")) {
+            let file = archive.by_index(i)?;
+            let json: Value = serde_json::from_reader(file).expect("should be JSON");
+
+            if let Some(main) = json.get("main")
+                && let Some(langs) = main.as_object()
+                && let Some((lang, details)) = langs.iter().next()
+                && let Some(calendar) = details.pointer("/dates/calendars/gregorian")
             {
-                decimals.insert(lang.clone(), decimal);
+                dbg!(lang);
+                let gregorian = from_value::<Calendar>(calendar.clone()).unwrap();
+                calendars.insert(lang.clone(), gregorian);
             }
         }
     }
-    let decimals = decimals.iter().filter(|(name, decimal)| {
-        if let Some((stem, _)) = name.split_once('-')
-            && decimals.get(stem) == Some(*decimal)
-        {
-            false
-        } else {
-            true
+    write_locales(&numbers, &calendars)?;
+
+    Ok(())
+}
+
+/// Read `src/spv/read/graph/patterns.svg` and write
+/// `src/spv/read/graph/patterns/data.rs`.
+fn build_patterns() -> AnyResult<()> {
+    let input_file = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/spv/read/graph/patterns.svg");
+    println!("cargo:rerun-if-changed={}", input_file.display());
+    let input = read_to_string(&input_file)
+        .map_err(|e| anyhow!("{}: read failed ({e})", input_file.display()))?;
+
+    let mut reader = quick_xml::Reader::from_str(&input);
+    let mut patterns = vec![None; 30];
+    reader.config_mut().expand_empty_elements = true;
+    loop {
+        let event = reader.read_event().unwrap();
+
+        match &event {
+            Event::Start(start) => {
+                let local_name = start.local_name();
+                if local_name.as_ref() == b"pattern" {
+                    let span = reader.read_to_end(start.to_end().name()).unwrap();
+                    let content = &input[span.start as usize..span.end as usize];
+                    let mut attributes = start
+                        .attributes()
+                        .map(|attribute| {
+                            let attribute = attribute.unwrap();
+                            (
+                                String::from_utf8(Vec::from(attribute.key.0)).unwrap(),
+                                String::from_utf8(attribute.value.into_owned()).unwrap(),
+                            )
+                        })
+                        .filter(|(key, _value)| key != "gifindex" && key != "fill")
+                        .collect::<BTreeMap<_, _>>();
+                    let id = attributes.remove("id").unwrap();
+                    let height = attributes.remove("height").unwrap().parse::<f64>().unwrap();
+                    let width = attributes.remove("width").unwrap().parse::<f64>().unwrap();
+                    let index = id
+                        .strip_prefix("pattern")
+                        .unwrap()
+                        .parse::<usize>()
+                        .unwrap();
+                    patterns[index] = Some((width, height, attributes, content));
+                    //writeln!(&mut output, "{index} {attributes:?} {content:?}").unwrap();
+                }
+            }
+            Event::Eof => break,
+            _ => (),
         }
-    });
+    }
 
-    let mut contents = String::new();
-    write!(
-        &mut contents,
-        "//! Autogenerated, do not edit by hand!
-//!
-//! The code in this file is generated by `build.rs`.  It is unconventional
-//! to check generated files into a repository, but it is done in this case
-//! because the source data is large and the generated code rarely needs to
-//! change.
-//!
-//! To regenerate this code, download a CLDR JSON release from
-//! <https://cldr.unicode.org/index/downloads>, rename it as
-//! `cldr-json-full.zip` in the same directory as `build.rs`,
-//! and touch `build.rs` to force a rebuild.
-use std::{{collections::HashMap, sync::LazyLock}};
-use crate::format::Decimal;
+    let patterns = patterns
+        .into_iter()
+        .map(|pattern| pattern.unwrap())
+        .collect::<Vec<_>>();
 
-/// Map from language to decimal point.
-pub static LANG_TO_DECIMAL: LazyLock<HashMap<&'static str, Decimal>> = LazyLock::new(|| {{
-    let mut map = HashMap::new();
-"
+    let mut output = String::new();
+    writeln!(
+        &mut output,
+        "use super::Pattern;
+
+pub static PATTERNS: [Pattern; 30] = ["
     )
     .unwrap();
-    for (lang, decimal) in decimals {
-        let decimal = if *decimal == ',' { "Comma" } else { "Dot" };
+    for (width, height, attributes, content) in &patterns {
         writeln!(
-            &mut contents,
-            "    map.insert({lang:?}, Decimal::{decimal});"
+            &mut output,
+            "    Pattern {{
+        width: {width:?},
+        height: {height:?},
+        attributes: &["
+        )
+        .unwrap();
+        for (name, value) in attributes {
+            writeln!(&mut output, "            ({name:?}, {value:?}),").unwrap();
+        }
+        writeln!(
+            &mut output,
+            "        ],
+        content: {content:?},
+    }},"
         )
         .unwrap();
     }
-    write!(
-        &mut contents,
-        "    map
-}});
-"
+    writeln!(&mut output, "];").unwrap();
+
+    let output_file_name =
+        Path::new(env!("CARGO_MANIFEST_DIR")).join("src/spv/read/graph/patterns/data.rs");
+    std::fs::write(output_file_name, &output)?;
+
+    Ok(())
+}
+
+/// Read `src/spv/read/graph/symbols.svg` and write
+/// `src/spv/read/graph/symbols/data.rs`.
+fn build_symbols() -> AnyResult<()> {
+    fn title_case(s: &str) -> String {
+        let mut iter = s.chars();
+        let first = iter.next().unwrap().to_ascii_uppercase();
+        let rest = iter.as_str();
+        format!("{first}{rest}")
+    }
+
+    let input_file = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/spv/read/graph/symbols.svg");
+    println!("cargo:rerun-if-changed={}", input_file.display());
+    let input = read_to_string(&input_file)
+        .map_err(|e| anyhow!("{}: read failed ({e})", input_file.display()))?;
+
+    let mut reader = quick_xml::Reader::from_str(&input);
+    let mut symbols = Vec::new();
+    reader.config_mut().expand_empty_elements = true;
+    let mut in_defs = false;
+    loop {
+        let event = reader.read_event().unwrap();
+
+        match &event {
+            Event::Start(start) => {
+                let local_name = start.local_name();
+                if local_name.as_ref() == b"defs" {
+                    in_defs = true;
+                } else if in_defs && local_name.as_ref() == b"g" {
+                    let span = reader.read_to_end(start.to_end().name()).unwrap();
+                    let content = input[span.start as usize..span.end as usize].trim_matches('\n');
+                    let mut attributes = start
+                        .attributes()
+                        .map(|attribute| {
+                            let attribute = attribute.unwrap();
+                            (
+                                String::from_utf8(Vec::from(attribute.key.0)).unwrap(),
+                                String::from_utf8(attribute.value.into_owned()).unwrap(),
+                            )
+                        })
+                        .collect::<BTreeMap<_, _>>();
+                    let name = attributes.remove("id").unwrap();
+                    let hollow = match attributes.remove("hollow").unwrap().as_str() {
+                        "true" => true,
+                        "false" => false,
+                        _ => unreachable!(),
+                    };
+                    let kind = attributes.remove("kind").unwrap_or_default();
+                    let sides = attributes
+                        .remove("sides")
+                        .map(|sides| sides.parse::<usize>().unwrap());
+                    symbols.push((name.clone(), hollow, kind.clone(), sides, content));
+
+                    if !hollow {
+                        symbols.push((
+                            format!("hollow{}", title_case(&name)),
+                            true,
+                            kind,
+                            sides,
+                            content,
+                        ));
+                    }
+                }
+            }
+            Event::End(end) => {
+                if end.local_name().as_ref() == b"defs" {
+                    in_defs = false;
+                }
+            }
+            Event::Eof => break,
+            _ => (),
+        }
+    }
+
+    let mut output = String::new();
+    writeln!(
+        &mut output,
+        "use super::{{Symbol, SymbolKind}};
+
+pub static SYMBOLS: [Symbol; {}] = [",
+        symbols.len()
     )
     .unwrap();
+    for (name, hollow, kind, sides, content) in &symbols {
+        let kind = if kind == "" {
+            format!("SymbolKind::Fixed({content:?})")
+        } else {
+            format!("SymbolKind::{}({})", title_case(&kind), sides.unwrap())
+        };
 
-    let output_file_name = Path::new("src/format/decimals.rs");
-    if !output_file_name.try_exists()? || contents.as_bytes() != &std::fs::read(&output_file_name)?
-    {
-        std::fs::write(&output_file_name, &contents)?;
+        writeln!(
+            &mut output,
+            "    Symbol {{
+        name: {name:?},
+        hollow: {hollow:?},
+        kind: {kind},
+    }},"
+        )
+        .unwrap();
     }
+    writeln!(&mut output, "];").unwrap();
+
+    let output_file_name =
+        Path::new(env!("CARGO_MANIFEST_DIR")).join("src/spv/read/graph/symbols/data.rs");
+    std::fs::write(output_file_name, &output)?;
 
     Ok(())
 }
@@ -305,7 +857,9 @@ fn main() -> AnyResult<()> {
     println!("cargo:rerun-if-changed=build.rs");
 
     build_encodings()?;
-    builld_decimals()?;
+    build_patterns()?;
+    build_symbols()?;
+    build_locales()?;
 
     Ok(())
 }
index d36442a35fe423a843df776d2b6a942eba9b35fa..51f0fd2c1ff8b72b72722cebbb376139e7a8625c 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 92ddb87d1b1ebf93fa67fd43a1bba87ddba81fb0..20b56c305e429e8d5dbfb6174eefd1a1232f8ee3 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 1a90637f56a4f868dae096bbc53435aaacb02fb4..9c1db48d89c26da20597fb7fd00381c5d18f0199 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
+use std::time::Duration;
+
 /// Dates and times in PSPP.
 ///
 /// PSPP represents dates as the number of seconds since [EPOCH], and times as
 /// the number of seconds since midnight.
-use chrono::{Datelike, Days, Month, NaiveDate, NaiveDateTime, NaiveTime};
+use chrono::{Datelike, Days, Month, NaiveDate, NaiveDateTime, NaiveTime, TimeDelta};
 use num::FromPrimitive;
 use thiserror::Error as ThisError;
 
@@ -49,6 +51,18 @@ pub fn calendar_offset_to_gregorian(offset: f64) -> Option<NaiveDate> {
     }
 }
 
+/// Takes a count of seconds from 14 Oct 1582 and translates it into
+/// [NaiveDateTime], if possible.  Positive and negative offsets are supported.
+pub fn calendar_offset_to_date_time(offset: f64) -> Option<NaiveDateTime> {
+    let duration = Duration::try_from_secs_f64(offset.abs()).ok()?;
+    let delta = TimeDelta::from_std(duration).ok()?;
+    if offset.is_sign_negative() {
+        EPOCH_DATETIME.checked_sub_signed(delta)
+    } else {
+        EPOCH_DATETIME.checked_add_signed(delta)
+    }
+}
+
 /// Returns the day of the year, where January 1 is day 1.
 pub fn day_of_year(date: NaiveDate) -> Option<u32> {
     let january1 = NaiveDate::from_ymd_opt(date.year(), 1, 1)?;
index 3a5868608fc1b9cabf6a7a326250f792c336e23a..a8f5af3d8416cdeaa323b10f828dd5017e208a71 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -22,18 +22,18 @@ use thiserror::Error as ThisError;
 use convert::Convert;
 use decrypt::Decrypt;
 use identify::Identify;
-use show::Show;
-use show_pc::ShowPc;
-use show_por::ShowPor;
-use show_spv::ShowSpv;
+use pc::Pc;
+use por::Por;
+use show::Sav;
+use spv::Spv;
 
 mod convert;
 mod decrypt;
 mod identify;
+mod pc;
+mod por;
 mod show;
-mod show_pc;
-mod show_por;
-mod show_spv;
+mod spv;
 
 /// PSPP, a program for statistical analysis of sampled data.
 #[derive(Parser, Debug)]
@@ -48,10 +48,14 @@ pub enum Command {
     Convert(Convert),
     Decrypt(Decrypt),
     Identify(Identify),
-    Show(Show),
-    ShowPor(ShowPor),
-    ShowPc(ShowPc),
-    ShowSpv(ShowSpv),
+    #[command(subcommand, alias = "sav")]
+    Show(Sav),
+    #[command(subcommand)]
+    Por(Por),
+    #[command(subcommand)]
+    Pc(Pc),
+    #[command(subcommand)]
+    Spv(Spv),
 }
 
 impl Command {
@@ -61,9 +65,9 @@ impl Command {
             Command::Decrypt(decrypt) => decrypt.run(),
             Command::Identify(identify) => identify.run(),
             Command::Show(show) => show.run(),
-            Command::ShowPor(show_por) => show_por.run(),
-            Command::ShowPc(show_pc) => show_pc.run(),
-            Command::ShowSpv(show_spv) => show_spv.run(),
+            Command::Por(show_por) => show_por.run(),
+            Command::Pc(show_pc) => show_pc.run(),
+            Command::Spv(spv) => spv.run(),
         }
     }
 }
index 803d27273231c3529bd075473830ccb05e72a4ed..f70f2474cf4f877650a0458374dcc29790b6d710 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@ use pspp::{
     data::{ByteString, Case, Datum},
     dictionary::Dictionary,
     file::FileType,
-    output::{Criteria, drivers::Driver},
+    output::{Criteria, Item, drivers::Driver},
     pc::PcFile,
     por::PortableFile,
     spv::SpvArchive,
@@ -32,24 +32,43 @@ use pspp::{
 
 use super::parse_encoding;
 
-/// Convert SPSS data and viewer files into other formats.
+/// Convert SPSS files into other formats.
 #[derive(Args, Clone, Debug)]
 pub struct Convert {
-    /// Input file name.
-    input: PathBuf,
+    /// Name of input file, one of the following types:
+    ///
+    /// * SPSS data files:
+    ///   - SPSS system files (usually `.sav` or `.sys`).
+    ///   - SPSS portable files (usually `.por`).
+    ///   - SPSS/PC+ system files (usually `.sys`).
+    ///
+    /// * SPSS viewer files (`.spv`).
+    ///
+    /// * SPSS exported chart files (usually `.xml`).
+    #[arg(verbatim_doc_comment)]
+    input: Vec<PathBuf>,
 
     /// Output file name (if omitted, output is written to stdout).
+    ///
+    /// When input is an SPSS data file, the output file may be in any of these
+    /// formats:
+    /// * Comma-separated value (CSV) file.
+    /// * SPSS system file.
+    /// * SPSS portable file (an obsolete format).
+    ///
+    /// When input is an SPSS viewer (`.spv`) or exported chart file, the output
+    /// file may be any PSPP output format, including:
+    /// * plain text
+    /// * PDF
+    /// * HTML
+    #[arg(verbatim_doc_comment, long, short = 'o')]
     output: Option<PathBuf>,
 
     /// The encoding to use for reading the input file.
     #[arg(short = 'e', long, value_parser = parse_encoding)]
     encoding: Option<&'static Encoding>,
 
-    /// Password for decryption.
-    ///
-    /// In addition to file encryption, SPSS supports a feature called "password
-    /// encryption".  The password specified can be specified with or without
-    /// "password encryption".
+    /// Password for decryption, with or without what SPSS calls "password encryption".
     ///
     /// Specify only for an encrypted system file.
     #[clap(short, long)]
@@ -67,7 +86,7 @@ pub struct Convert {
     to_unicode: bool,
 
     /// Output driver configuration options.
-    #[arg(short = 'o')]
+    #[arg(short = 'O')]
     output_options: Vec<String>,
 
     /// Selection options for SPV input files.
@@ -84,6 +103,10 @@ impl Convert {
         dictionary: Dictionary,
         cases: Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>,
     ) -> Result<()> {
+        if self.input.len() > 1 {
+            bail!("'convert' only supports a single data file input");
+        }
+
         // Take only the first `self.max_cases` cases.
         let cases = cases.take(self.max_cases.unwrap_or(usize::MAX));
 
@@ -100,67 +123,92 @@ impl Convert {
     }
 
     pub fn run(self) -> Result<()> {
-        match FileType::from_file(&self.input)? {
-            Some(FileType::System { .. }) => {
-                fn warn(warning: anyhow::Error) {
-                    eprintln!("warning: {warning}");
-                }
+        let mut data = None;
+        let mut page_setup = None;
+        let mut items = Vec::new();
 
-                let mut system_file = ReadOptions::new(warn)
-                    .with_encoding(self.encoding)
-                    .with_password(self.password.clone())
-                    .open_file(&self.input)?;
-                if self.to_unicode {
-                    system_file = system_file.into_unicode();
-                }
-                let (dictionary, _, cases) = system_file.into_parts();
-                let cases = cases.map(|result| result.map_err(AnyError::from));
-                let cases = Box::new(cases)
-                    as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
-                self.write_data(dictionary, cases)
+        for input in &self.input {
+            if data.is_some() {
+                bail!("convert: Only one data file may be specified.");
             }
-            Some(FileType::Portable) => {
-                fn warn_portable(warning: pspp::por::Warning) {
-                    eprintln!("warning: {warning}");
-                }
 
-                let portable_file = PortableFile::open_file(&self.input, warn_portable)?;
-                let (dictionary, _, cases) = portable_file.into_parts();
-                let cases = cases.map(|result| result.map_err(AnyError::from));
-                let cases = Box::new(cases)
-                    as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
-                self.write_data(dictionary, cases)
-            }
-            Some(FileType::Pc) => {
-                fn warn_pc(warning: pspp::pc::Warning) {
-                    eprintln!("warning: {warning}");
+            match FileType::from_file(input)? {
+                Some(FileType::System { .. }) => {
+                    fn warn(warning: anyhow::Error) {
+                        eprintln!("warning: {warning}");
+                    }
+
+                    let mut system_file = ReadOptions::new(warn)
+                        .with_encoding(self.encoding)
+                        .with_password(self.password.clone())
+                        .open_file(input)?;
+                    if self.to_unicode {
+                        system_file = system_file.into_unicode();
+                    }
+                    let (dictionary, _, cases) = system_file.into_parts();
+                    let cases = cases.map(|result| result.map_err(AnyError::from));
+                    let cases = Box::new(cases)
+                        as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
+                    data = Some((dictionary, cases));
                 }
-
-                let pc_file = PcFile::open_file(&self.input, warn_pc)?;
-                let (dictionary, _, cases) = pc_file.into_parts();
-                let cases = cases.map(|result| result.map_err(AnyError::from));
-                let cases = Box::new(cases)
-                    as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
-                self.write_data(dictionary, cases)
-            }
-            Some(FileType::Viewer { .. }) => {
-                let (items, page_setup) =
-                    SpvArchive::open_file(&self.input, self.password.as_deref())?
-                        .read(|e| eprintln!("{e}"))?
+                Some(FileType::Portable) => {
+                    fn warn_portable(warning: pspp::por::Warning) {
+                        eprintln!("warning: {warning}");
+                    }
+
+                    let portable_file = PortableFile::open_file(input, warn_portable)?;
+                    let (dictionary, _, cases) = portable_file.into_parts();
+                    let cases = cases.map(|result| result.map_err(AnyError::from));
+                    let cases = Box::new(cases)
+                        as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
+                    data = Some((dictionary, cases));
+                }
+                Some(FileType::Pc) => {
+                    fn warn_pc(warning: pspp::pc::Warning) {
+                        eprintln!("warning: {warning}");
+                    }
+
+                    let pc_file = PcFile::open_file(input, warn_pc)?;
+                    let (dictionary, _, cases) = pc_file.into_parts();
+                    let cases = cases.map(|result| result.map_err(AnyError::from));
+                    let cases = Box::new(cases)
+                        as Box<dyn Iterator<Item = Result<Case<Vec<Datum<ByteString>>>, AnyError>>>;
+                    data = Some((dictionary, cases));
+                }
+                Some(FileType::Viewer { .. }) => {
+                    let mut archive = SpvArchive::open_file(input, self.password.as_deref())?;
+                    let (mut archive_items, archive_page_setup) = archive
+                        .read_outline(|e| eprintln!("{e}"))?
+                        .filter(&self.criteria)
+                        .read(&mut archive, &mut |e| eprintln!("{e}"))?
                         .into_contents();
-                let mut output = self.open_driver("text")?;
-                if let Some(page_setup) = &page_setup {
-                    output.setup(page_setup);
+                    page_setup = page_setup.or(archive_page_setup);
+                    items.append(&mut archive_items);
                 }
-                for item in items {
-                    output.write(&Arc::new(item));
+                Some(FileType::Graph) => {
+                    let graph = Item::read_graph(input)?;
+                    items.push(graph);
                 }
-                Ok(())
+                Some(other) => bail!("{}: cannot convert {other} file", input.display()),
+                _ => bail!("{}: unknown input file format", input.display()),
+            }
+        }
+
+        if data.is_some() && !items.is_empty() {
+            bail!("convert: Data and viewer files may not both be specified.");
+        }
+
+        if let Some((dictionary, cases)) = data {
+            self.write_data(dictionary, cases)
+        } else {
+            let mut output = self.open_driver("text")?;
+            if let Some(page_setup) = &page_setup {
+                output.setup(page_setup);
+            }
+            for item in items {
+                output.write(&Arc::new(item));
             }
-            _ => bail!(
-                "{}: not a system, portable, or SPSS/PC+ file",
-                self.input.display()
-            ),
+            Ok(())
         }
     }
 }
index dde16d27f2d6e0ab5131595eb4d2aca22ab1d311..9a0e35ad0e26f995750e44932f0cf8134f273347 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
- * Copyright (C) 2025 Free Software Foundation, Inc.
+ * Copyright (C) 2025, 2026 Free Software Foundation, Inc.
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 917e86370bcbdc5b9a9ac09a46dc7d33870bf604..102955ff01bc1978bf5f6051c895128ffc528900 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
diff --git a/rust/pspp/src/cli/pc.rs b/rust/pspp/src/cli/pc.rs
new file mode 100644 (file)
index 0000000..420d8cf
--- /dev/null
@@ -0,0 +1,187 @@
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use anyhow::{Result, anyhow};
+use clap::{Args, Subcommand};
+use itertools::Itertools;
+use pspp::{
+    data::cases_to_output,
+    output::{Item, Text, drivers::Driver, pivot::PivotTable},
+    pc::PcFile,
+};
+use serde::Serialize;
+use std::{cell::RefCell, fmt::Display, fs::File, io::BufReader, path::PathBuf, rc::Rc, sync::Arc};
+
+/// Examine SPSS/PC+ data files.
+#[derive(Subcommand, Clone, Debug)]
+pub enum Pc {
+    /// File dictionary, with variables, value labels, ...
+    #[command(alias = "dict")]
+    Dictionary(Dictionary),
+
+    /// File metadata not included in the dictionary.
+    Metadata(Metadata),
+}
+
+/// Show the file dictionary, with variables, value labels, and more.
+#[derive(Args, Clone, Debug)]
+pub struct Dictionary {
+    /// File to read.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// Maximum number of cases to read.
+    ///
+    /// If specified without an argument, all cases will be read.
+    #[arg(
+        long = "data",
+        num_args = 0..=1,
+        default_missing_value = "18446744073709551615",
+        default_value_t = 0,
+    )]
+    max_cases: usize,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O')]
+    output_options: Vec<String>,
+}
+
+/// Show the file dictionary, with variables, value labels, and more.
+#[derive(Args, Clone, Debug)]
+pub struct Metadata {
+    /// File to read.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O')]
+    output_options: Vec<String>,
+}
+
+struct Output {
+    driver: Rc<RefCell<Box<dyn Driver>>>,
+}
+
+impl Output {
+    fn can_show_json(&self) -> bool {
+        self.driver.borrow().can_serialize()
+    }
+
+    fn show_json<T>(&self, value: &T) -> Result<()>
+    where
+        T: Serialize,
+    {
+        let mut driver = self.driver.borrow_mut();
+        if driver.can_serialize() {
+            driver.serialize(value);
+            Ok(())
+        } else {
+            Err(anyhow!("This command only supports output as JSON."))
+        }
+    }
+
+    fn warn(&self, warning: &impl Display) {
+        let mut driver = self.driver.borrow_mut();
+        #[derive(Serialize)]
+        struct Warning {
+            warning: String,
+        }
+        let w = Warning {
+            warning: warning.to_string(),
+        };
+        if driver.can_serialize() {
+            driver.serialize(&w);
+        } else {
+            driver.write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
+        }
+    }
+}
+
+impl Pc {
+    pub fn run(self) -> Result<()> {
+        match self {
+            Pc::Dictionary(dictionary) => dictionary.run(),
+            Pc::Metadata(metadata) => metadata.run(),
+        }
+    }
+}
+
+impl Dictionary {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = BufReader::new(File::open(&self.input)?);
+        let PcFile {
+            dictionary,
+            metadata: _,
+            cases,
+        } = PcFile::open(reader, |warning| output.warn(&warning))?;
+        let cases = cases.take(self.max_cases);
+
+        if output.can_show_json() {
+            output.show_json(&dictionary)?;
+            for (_index, case) in (0..self.max_cases).zip(cases) {
+                output.show_json(&case?)?;
+            }
+        } else {
+            let mut items = Vec::new();
+            items.extend(dictionary.all_pivot_tables().into_iter().map_into());
+            items.extend(cases_to_output(&dictionary, cases));
+            output
+                .driver
+                .borrow_mut()
+                .write(&Arc::new(items.into_iter().collect()));
+        }
+        Ok(())
+    }
+}
+
+impl Metadata {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = BufReader::new(File::open(&self.input)?);
+        let metadata = PcFile::open(reader, |warning| output.warn(&warning))?.metadata;
+
+        if output.can_show_json() {
+            output.show_json(&metadata)?;
+        } else {
+            output
+                .driver
+                .borrow_mut()
+                .write(&Arc::new(PivotTable::from(&metadata).into()));
+        }
+        Ok(())
+    }
+}
diff --git a/rust/pspp/src/cli/por.rs b/rust/pspp/src/cli/por.rs
new file mode 100644 (file)
index 0000000..0cadb4b
--- /dev/null
@@ -0,0 +1,242 @@
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use anyhow::{Result, anyhow};
+use clap::{Args, Subcommand};
+use itertools::Itertools;
+use pspp::{
+    data::cases_to_output,
+    output::{Item, Text, drivers::Driver, pivot::PivotTable},
+    por::PortableFile,
+};
+use serde::Serialize;
+use std::{cell::RefCell, fmt::Display, fs::File, io::BufReader, path::PathBuf, rc::Rc, sync::Arc};
+
+/// Examine SPSS portable files.
+#[derive(Subcommand, Clone, Debug)]
+pub enum Por {
+    /// File dictionary, with variables, value labels, and more.
+    #[command(alias = "dict")]
+    Dictionary(Dictionary),
+
+    /// File metadata not included in the dictionary.
+    Metadata(Metadata),
+
+    /// Histogram of character incidence in the file.
+    Histogram(Histogram),
+}
+
+impl Por {
+    pub fn run(self) -> Result<()> {
+        match self {
+            Por::Dictionary(dictionary) => dictionary.run(),
+            Por::Metadata(metadata) => metadata.run(),
+            Por::Histogram(histogram) => histogram.run(),
+        }
+    }
+}
+
+/// Show file dictionary, with variables, value labels, and more.
+#[derive(Args, Clone, Debug)]
+pub struct Dictionary {
+    /// File to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// Maximum number of cases to read.
+    ///
+    /// If specified without an argument, all cases will be read.
+    #[arg(
+        long = "data",
+        num_args = 0..=1,
+        default_missing_value = "18446744073709551615",
+        default_value_t = 0,
+        help_heading = "Input file options"
+    )]
+    max_cases: usize,
+
+    /// Output driver configuration options.
+    #[arg(short = 'o', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+struct Output {
+    driver: Rc<RefCell<Box<dyn Driver>>>,
+}
+
+impl Output {
+    fn can_show_json(&self) -> bool {
+        self.driver.borrow().can_serialize()
+    }
+
+    fn show_json<T>(&self, value: &T) -> Result<()>
+    where
+        T: Serialize,
+    {
+        let mut driver = self.driver.borrow_mut();
+        if driver.can_serialize() {
+            driver.serialize(value);
+            Ok(())
+        } else {
+            Err(anyhow!("This command only supports output as JSON."))
+        }
+    }
+
+    fn warn(&self, warning: &impl Display) {
+        let mut driver = self.driver.borrow_mut();
+        #[derive(Serialize)]
+        struct Warning {
+            warning: String,
+        }
+        let w = Warning {
+            warning: warning.to_string(),
+        };
+        if driver.can_serialize() {
+            driver.serialize(&w);
+        } else {
+            driver.write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
+        }
+    }
+}
+
+impl Dictionary {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = BufReader::new(File::open(&self.input)?);
+        let PortableFile {
+            dictionary,
+            metadata: _,
+            cases,
+        } = PortableFile::open(reader, |warning| output.warn(&warning))?;
+        let cases = cases.take(self.max_cases);
+
+        if output.can_show_json() {
+            output.show_json(&dictionary)?;
+            for (_index, case) in (0..self.max_cases).zip(cases) {
+                output.show_json(&case?)?;
+            }
+        } else {
+            let mut items = Vec::new();
+            items.extend(dictionary.all_pivot_tables().into_iter().map_into());
+            items.extend(cases_to_output(&dictionary, cases));
+            output
+                .driver
+                .borrow_mut()
+                .write(&Arc::new(items.into_iter().collect()));
+        }
+        Ok(())
+    }
+}
+
+/// Show portable file metadata not included in the dictionary.
+#[derive(Args, Clone, Debug)]
+pub struct Metadata {
+    /// File to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+impl Metadata {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = BufReader::new(File::open(&self.input)?);
+        let metadata = PortableFile::open(reader, |warning| output.warn(&warning))?.metadata;
+
+        if output.can_show_json() {
+            output.show_json(&metadata)?;
+        } else {
+            output
+                .driver
+                .borrow_mut()
+                .write(&Arc::new(PivotTable::from(&metadata).into()));
+        }
+        Ok(())
+    }
+}
+
+/// Show histogram of character incidence in a portable file.
+#[derive(Args, Clone, Debug)]
+pub struct Histogram {
+    /// File to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+impl Histogram {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = BufReader::new(File::open(&self.input)?);
+        let (histogram, translations) = PortableFile::read_histogram(reader)?;
+        let h = histogram
+            .into_iter()
+            .enumerate()
+            .filter_map(|(index, count)| {
+                if count > 0
+                    && index != translations[index as u8] as usize
+                    && translations[index as u8] != 0
+                {
+                    Some((
+                        format!("{index:02x}"),
+                        translations[index as u8] as char,
+                        count,
+                    ))
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
+        output.show_json(&h)?;
+        Ok(())
+    }
+}
index 038eac08741328fe91c58f91a05968df0f66e895..392cf029b21ddc0e1dd97cd5b1a986a739746d45 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -16,7 +16,7 @@
 
 use super::parse_encoding;
 use anyhow::{Result, anyhow};
-use clap::{Args, ValueEnum};
+use clap::{Args, Subcommand};
 use encoding_rs::Encoding;
 use itertools::Itertools;
 use pspp::{
@@ -30,14 +30,194 @@ use pspp::{
 use serde::Serialize;
 use std::{cell::RefCell, fmt::Display, fs::File, io::BufReader, path::PathBuf, rc::Rc, sync::Arc};
 
+/// Examine SPSS system files.
+#[derive(Subcommand, Clone, Debug)]
+pub enum Sav {
+    Identity(Identity),
+    #[command(alias = "dict")]
+    Dictionary(Dictionary),
+    Encodings(Encodings),
+    Raw(Raw),
+    Decoded(Decoded),
+}
+
+impl Sav {
+    pub fn run(self) -> Result<()> {
+        match self {
+            Sav::Identity(identity) => identity.run(),
+            Sav::Dictionary(dictionary) => dictionary.run(),
+            Sav::Encodings(encodings) => encodings.run(),
+            Sav::Raw(raw) => raw.run(),
+            Sav::Decoded(decoded) => decoded.run(),
+        }
+    }
+}
+
 /// Show information about SPSS system files.
 #[derive(Args, Clone, Debug)]
-pub struct Show {
-    /// What to show.
-    #[arg(value_enum)]
-    mode: Mode,
+pub struct Identity {
+    /// `.sav` file to identify.
+    #[arg(required = true)]
+    input: PathBuf,
+}
+
+impl Identity {
+    pub fn run(self) -> Result<()> {
+        let reader = File::open(&self.input)?;
+        let reader = BufReader::new(reader);
+        let reader = Reader::new(reader, Box::new(|warning| eprintln!("{warning}")))?;
+
+        match reader.header().magic {
+            Magic::Sav => println!("SPSS System File"),
+            Magic::Zsav => println!("SPSS System File with Zlib compression"),
+            Magic::Ebcdic => println!("EBCDIC-encoded SPSS System File"),
+        }
+        Ok(())
+    }
+}
+
+/// Show file dictionary, with variables, value labels, attributes, and more.
+#[derive(Args, Clone, Debug)]
+pub struct Dictionary {
+    /// `.sav` file to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// The encoding to use.
+    #[arg(long, value_parser = parse_encoding, help_heading = "Input file options")]
+    encoding: Option<&'static Encoding>,
+
+    /// Show the specified number of cases in addition to dictionary
+    /// information.
+    ///
+    /// If specified without an argument, all cases will be read.
+    ///
+    /// `pspp convert` is a better way to convert data to another format.
+    #[arg(
+        long = "data",
+        num_args = 0..=1,
+        default_missing_value = "18446744073709551615",
+        default_value_t = 0,
+        help_heading = "Input file options"
+    )]
+    max_cases: u64,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+impl Dictionary {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = File::open(&self.input)?;
+        let reader = BufReader::new(reader);
+        let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
+
+        let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
+        let encoding = match self.encoding {
+            Some(encoding) => encoding,
+            None => infer_encoding(&records, &mut |e| output.warn(&e))?,
+        };
+        let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
+        let records = Records::from_raw(records, &mut decoder);
+        let (dictionary, metadata, cases) = records
+            .decode(
+                reader.header().clone().decode(&mut decoder),
+                reader.cases(),
+                encoding,
+                |e| output.warn(&e),
+            )
+            .into_parts();
+
+        if output.can_show_json() {
+            output.show_json(&dictionary)?;
+            output.show_json(&metadata)?;
+            for (_index, case) in (0..self.max_cases).zip(cases) {
+                output.show_json(&case?)?;
+            }
+        } else {
+            let mut items = Vec::new();
+            items.push(PivotTable::from(&metadata).into());
+            items.extend(dictionary.all_pivot_tables().into_iter().map_into());
+            items.extend(cases_to_output(&dictionary, cases));
+            output
+                .driver
+                .borrow_mut()
+                .write(&Arc::new(items.into_iter().collect()));
+        }
+        Ok(())
+    }
+}
+
+/// Show possible encodings of text in file dictionary.
+///
+/// With `--data`, also considers text in cases.
+#[derive(Args, Clone, Debug)]
+pub struct Encodings {
+    /// `.sav` file to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// The encoding to use.
+    #[arg(long, value_parser = parse_encoding, help_heading = "Input file options")]
+    encoding: Option<&'static Encoding>,
+
+    /// Maximum number of cases to read.
+    ///
+    /// If specified without an argument, all cases will be read.
+    #[arg(
+        long = "data",
+        num_args = 0..=1,
+        default_missing_value = "18446744073709551615",
+        default_value_t = 0,
+        help_heading = "Input file options"
+    )]
+    max_cases: u64,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+impl Encodings {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = File::open(&self.input)?;
+        let reader = BufReader::new(reader);
+        let reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
 
-    /// File to show.
+        let encoding_report = EncodingReport::new(reader, self.max_cases)?;
+        output.show(&encoding_report)?;
+
+        Ok(())
+    }
+}
+
+/// Show raw file records, without assuming a particular character encoding.
+#[derive(Args, Clone, Debug)]
+pub struct Raw {
+    /// `.sav` file to show.
     #[arg(required = true)]
     input: PathBuf,
 
@@ -50,6 +230,8 @@ pub struct Show {
 
     /// Maximum number of cases to read.
     ///
+    /// If not specified, cases will not be read.
+    ///
     /// If specified without an argument, all cases will be read.
     #[arg(
         long = "data",
@@ -61,13 +243,85 @@ pub struct Show {
     max_cases: u64,
 
     /// Output driver configuration options.
-    #[arg(short = 'o', help_heading = "Output options")]
+    #[arg(short = 'O', help_heading = "Output options")]
+    output_options: Vec<String>,
+}
+
+impl Raw {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = File::open(&self.input)?;
+        let reader = BufReader::new(reader);
+        let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
+
+        output.show_json(reader.header())?;
+        for record in reader.records() {
+            output.show_json(&record?)?;
+        }
+        for (_index, case) in (0..self.max_cases).zip(reader.cases()) {
+            output.show_json(&case?)?;
+        }
+
+        Ok(())
+    }
+}
+
+/// Show raw file records decoded with a particular character encoding.
+#[derive(Args, Clone, Debug)]
+pub struct Decoded {
+    /// `.sav` file to show.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output: Option<PathBuf>,
+
+    /// The encoding to use.
+    #[arg(long, value_parser = parse_encoding, help_heading = "Input file options")]
+    encoding: Option<&'static Encoding>,
+
+    /// Output driver configuration options.
+    #[arg(short = 'O', help_heading = "Output options")]
     output_options: Vec<String>,
 }
 
+impl Decoded {
+    pub fn run(self) -> Result<()> {
+        let output = Output {
+            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
+                self.output.as_ref(),
+                &self.output_options,
+                "json",
+            )?))),
+        };
+
+        let reader = File::open(&self.input)?;
+        let reader = BufReader::new(reader);
+        let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
+
+        let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
+        let encoding = match self.encoding {
+            Some(encoding) => encoding,
+            None => infer_encoding(&records, &mut |e| output.warn(&e))?,
+        };
+        let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
+        for record in records {
+            output.show_json(&record.decode(&mut decoder))?;
+        }
+
+        Ok(())
+    }
+}
+
 struct Output {
     driver: Rc<RefCell<Box<dyn Driver>>>,
-    mode: Mode,
 }
 
 impl Output {
@@ -98,10 +352,7 @@ impl Output {
             driver.serialize(value);
             Ok(())
         } else {
-            Err(anyhow!(
-                "Mode '{}' only supports output as JSON.",
-                self.mode
-            ))
+            Err(anyhow!("This command only supports output as JSON."))
         }
     }
 
@@ -121,130 +372,3 @@ impl Output {
         }
     }
 }
-
-impl Show {
-    pub fn run(self) -> Result<()> {
-        let output = Output {
-            mode: self.mode,
-            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
-                self.output.as_ref(),
-                &self.output_options,
-                "json",
-            )?))),
-        };
-
-        let reader = File::open(&self.input)?;
-        let reader = BufReader::new(reader);
-        let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
-
-        match self.mode {
-            Mode::Identity => {
-                match reader.header().magic {
-                    Magic::Sav => println!("SPSS System File"),
-                    Magic::Zsav => println!("SPSS System File with Zlib compression"),
-                    Magic::Ebcdic => println!("EBCDIC-encoded SPSS System File"),
-                }
-                return Ok(());
-            }
-            Mode::Raw => {
-                output.show_json(reader.header())?;
-                for record in reader.records() {
-                    output.show_json(&record?)?;
-                }
-                for (_index, case) in (0..self.max_cases).zip(reader.cases()) {
-                    output.show_json(&case?)?;
-                }
-            }
-            Mode::Decoded => {
-                let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
-                let encoding = match self.encoding {
-                    Some(encoding) => encoding,
-                    None => infer_encoding(&records, &mut |e| output.warn(&e))?,
-                };
-                let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
-                for record in records {
-                    output.show_json(&record.decode(&mut decoder))?;
-                }
-            }
-            Mode::Dictionary => {
-                let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
-                let encoding = match self.encoding {
-                    Some(encoding) => encoding,
-                    None => infer_encoding(&records, &mut |e| output.warn(&e))?,
-                };
-                let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
-                let records = Records::from_raw(records, &mut decoder);
-                let (dictionary, metadata, cases) = records
-                    .decode(
-                        reader.header().clone().decode(&mut decoder),
-                        reader.cases(),
-                        encoding,
-                        |e| output.warn(&e),
-                    )
-                    .into_parts();
-
-                if output.can_show_json() {
-                    output.show_json(&dictionary)?;
-                    output.show_json(&metadata)?;
-                    for (_index, case) in (0..self.max_cases).zip(cases) {
-                        output.show_json(&case?)?;
-                    }
-                } else {
-                    let mut items = Vec::new();
-                    items.push(PivotTable::from(&metadata).into());
-                    items.extend(dictionary.all_pivot_tables().into_iter().map_into());
-                    items.extend(cases_to_output(&dictionary, cases));
-                    output
-                        .driver
-                        .borrow_mut()
-                        .write(&Arc::new(items.into_iter().collect()));
-                }
-            }
-            Mode::Encodings => {
-                let encoding_report = EncodingReport::new(reader, self.max_cases)?;
-                output.show(&encoding_report)?;
-            }
-        }
-
-        Ok(())
-    }
-}
-
-/// What to show in a system file.
-#[derive(Clone, Copy, Debug, Default, PartialEq, ValueEnum)]
-enum Mode {
-    /// The kind of file.
-    Identity,
-
-    /// File dictionary, with variables, value labels, attributes, ...
-    #[default]
-    #[value(alias = "dict")]
-    Dictionary,
-
-    /// Possible encodings of text in file dictionary and (with `--data`) cases.
-    Encodings,
-
-    /// Raw file records, without assuming a particular character encoding.
-    Raw,
-
-    /// Raw file records decoded with a particular character encoding.
-    Decoded,
-}
-
-impl Mode {
-    fn as_str(&self) -> &'static str {
-        match self {
-            Mode::Dictionary => "dictionary",
-            Mode::Identity => "identity",
-            Mode::Raw => "raw",
-            Mode::Decoded => "decoded",
-            Mode::Encodings => "encodings",
-        }
-    }
-}
-
-impl Display for Mode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.as_str())
-    }
-}
diff --git a/rust/pspp/src/cli/show_pc.rs b/rust/pspp/src/cli/show_pc.rs
deleted file mode 100644 (file)
index cb23fdc..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-// PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-// details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program.  If not, see <http://www.gnu.org/licenses/>.
-
-use anyhow::{Result, anyhow};
-use clap::{Args, ValueEnum};
-use itertools::Itertools;
-use pspp::{
-    data::cases_to_output,
-    output::{Item, Text, drivers::Driver, pivot::PivotTable},
-    pc::PcFile,
-};
-use serde::Serialize;
-use std::{cell::RefCell, fmt::Display, fs::File, io::BufReader, path::PathBuf, rc::Rc, sync::Arc};
-
-/// Show information about SPSS/PC+ data files.
-#[derive(Args, Clone, Debug)]
-pub struct ShowPc {
-    /// What to show.
-    #[arg(value_enum)]
-    mode: Mode,
-
-    /// File to show.
-    #[arg(required = true)]
-    input: PathBuf,
-
-    /// Output file name.  If omitted, output is written to stdout.
-    output: Option<PathBuf>,
-
-    /// Maximum number of cases to read.
-    ///
-    /// If specified without an argument, all cases will be read.
-    #[arg(
-        long = "data",
-        num_args = 0..=1,
-        default_missing_value = "18446744073709551615",
-        default_value_t = 0,
-    )]
-    max_cases: usize,
-
-    /// Output driver configuration options.
-    #[arg(short = 'o')]
-    output_options: Vec<String>,
-}
-
-struct Output {
-    driver: Rc<RefCell<Box<dyn Driver>>>,
-    mode: Mode,
-}
-
-impl Output {
-    fn can_show_json(&self) -> bool {
-        self.driver.borrow().can_serialize()
-    }
-
-    fn show_json<T>(&self, value: &T) -> Result<()>
-    where
-        T: Serialize,
-    {
-        let mut driver = self.driver.borrow_mut();
-        if driver.can_serialize() {
-            driver.serialize(value);
-            Ok(())
-        } else {
-            Err(anyhow!(
-                "Mode '{}' only supports output as JSON.",
-                self.mode
-            ))
-        }
-    }
-
-    fn warn(&self, warning: &impl Display) {
-        let mut driver = self.driver.borrow_mut();
-        #[derive(Serialize)]
-        struct Warning {
-            warning: String,
-        }
-        let w = Warning {
-            warning: warning.to_string(),
-        };
-        if driver.can_serialize() {
-            driver.serialize(&w);
-        } else {
-            driver.write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
-        }
-    }
-}
-
-impl ShowPc {
-    pub fn run(self) -> Result<()> {
-        let output = Output {
-            mode: self.mode,
-            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
-                self.output.as_ref(),
-                &self.output_options,
-                "json",
-            )?))),
-        };
-
-        let reader = BufReader::new(File::open(&self.input)?);
-        match self.mode {
-            Mode::Dictionary => {
-                let PcFile {
-                    dictionary,
-                    metadata: _,
-                    cases,
-                } = PcFile::open(reader, |warning| output.warn(&warning))?;
-                let cases = cases.take(self.max_cases);
-
-                if output.can_show_json() {
-                    output.show_json(&dictionary)?;
-                    for (_index, case) in (0..self.max_cases).zip(cases) {
-                        output.show_json(&case?)?;
-                    }
-                } else {
-                    let mut items = Vec::new();
-                    items.extend(dictionary.all_pivot_tables().into_iter().map_into());
-                    items.extend(cases_to_output(&dictionary, cases));
-                    output
-                        .driver
-                        .borrow_mut()
-                        .write(&Arc::new(items.into_iter().collect()));
-                }
-            }
-            Mode::Metadata => {
-                let metadata = PcFile::open(reader, |warning| output.warn(&warning))?.metadata;
-
-                if output.can_show_json() {
-                    output.show_json(&metadata)?;
-                } else {
-                    output
-                        .driver
-                        .borrow_mut()
-                        .write(&Arc::new(PivotTable::from(&metadata).into()));
-                }
-            }
-        }
-        Ok(())
-    }
-}
-
-/// What to show in a system file.
-#[derive(Clone, Copy, Debug, Default, PartialEq, ValueEnum)]
-enum Mode {
-    /// File dictionary, with variables, value labels, ...
-    #[default]
-    #[value(alias = "dict")]
-    Dictionary,
-
-    /// File metadata not included in the dictionary.
-    Metadata,
-}
-
-impl Mode {
-    fn as_str(&self) -> &'static str {
-        match self {
-            Mode::Dictionary => "dictionary",
-            Mode::Metadata => "metadata",
-        }
-    }
-}
-
-impl Display for Mode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.as_str())
-    }
-}
diff --git a/rust/pspp/src/cli/show_por.rs b/rust/pspp/src/cli/show_por.rs
deleted file mode 100644 (file)
index 96a5117..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-// PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-// details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program.  If not, see <http://www.gnu.org/licenses/>.
-
-use anyhow::{Result, anyhow};
-use clap::{Args, ValueEnum};
-use itertools::Itertools;
-use pspp::{
-    data::cases_to_output,
-    output::{Item, Text, drivers::Driver, pivot::PivotTable},
-    por::PortableFile,
-};
-use serde::Serialize;
-use std::{cell::RefCell, fmt::Display, fs::File, io::BufReader, path::PathBuf, rc::Rc, sync::Arc};
-
-/// Show information about SPSS portable files.
-#[derive(Args, Clone, Debug)]
-pub struct ShowPor {
-    /// What to show.
-    #[arg(value_enum)]
-    mode: Mode,
-
-    /// File to show.
-    #[arg(required = true)]
-    input: PathBuf,
-
-    /// Output file name.  If omitted, output is written to stdout.
-    output: Option<PathBuf>,
-
-    /// Maximum number of cases to read.
-    ///
-    /// If specified without an argument, all cases will be read.
-    #[arg(
-        long = "data",
-        num_args = 0..=1,
-        default_missing_value = "18446744073709551615",
-        default_value_t = 0,
-        help_heading = "Input file options"
-    )]
-    max_cases: usize,
-
-    /// Output driver configuration options.
-    #[arg(short = 'o', help_heading = "Output options")]
-    output_options: Vec<String>,
-}
-
-struct Output {
-    driver: Rc<RefCell<Box<dyn Driver>>>,
-    mode: Mode,
-}
-
-impl Output {
-    fn can_show_json(&self) -> bool {
-        self.driver.borrow().can_serialize()
-    }
-
-    fn show_json<T>(&self, value: &T) -> Result<()>
-    where
-        T: Serialize,
-    {
-        let mut driver = self.driver.borrow_mut();
-        if driver.can_serialize() {
-            driver.serialize(value);
-            Ok(())
-        } else {
-            Err(anyhow!(
-                "Mode '{}' only supports output as JSON.",
-                self.mode
-            ))
-        }
-    }
-
-    fn warn(&self, warning: &impl Display) {
-        let mut driver = self.driver.borrow_mut();
-        #[derive(Serialize)]
-        struct Warning {
-            warning: String,
-        }
-        let w = Warning {
-            warning: warning.to_string(),
-        };
-        if driver.can_serialize() {
-            driver.serialize(&w);
-        } else {
-            driver.write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
-        }
-    }
-}
-
-impl ShowPor {
-    pub fn run(self) -> Result<()> {
-        let output = Output {
-            mode: self.mode,
-            driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::from_options(
-                self.output.as_ref(),
-                &self.output_options,
-                "json",
-            )?))),
-        };
-
-        let reader = BufReader::new(File::open(&self.input)?);
-        match self.mode {
-            Mode::Dictionary => {
-                let PortableFile {
-                    dictionary,
-                    metadata: _,
-                    cases,
-                } = PortableFile::open(reader, |warning| output.warn(&warning))?;
-                let cases = cases.take(self.max_cases);
-
-                if output.can_show_json() {
-                    output.show_json(&dictionary)?;
-                    for (_index, case) in (0..self.max_cases).zip(cases) {
-                        output.show_json(&case?)?;
-                    }
-                } else {
-                    let mut items = Vec::new();
-                    items.extend(dictionary.all_pivot_tables().into_iter().map_into());
-                    items.extend(cases_to_output(&dictionary, cases));
-                    output
-                        .driver
-                        .borrow_mut()
-                        .write(&Arc::new(items.into_iter().collect()));
-                }
-            }
-            Mode::Metadata => {
-                let metadata =
-                    PortableFile::open(reader, |warning| output.warn(&warning))?.metadata;
-
-                if output.can_show_json() {
-                    output.show_json(&metadata)?;
-                } else {
-                    output
-                        .driver
-                        .borrow_mut()
-                        .write(&Arc::new(PivotTable::from(&metadata).into()));
-                }
-            }
-            Mode::Histogram => {
-                let (histogram, translations) = PortableFile::read_histogram(reader)?;
-                let h = histogram
-                    .into_iter()
-                    .enumerate()
-                    .filter_map(|(index, count)| {
-                        if count > 0
-                            && index != translations[index as u8] as usize
-                            && translations[index as u8] != 0
-                        {
-                            Some((
-                                format!("{index:02x}"),
-                                translations[index as u8] as char,
-                                count,
-                            ))
-                        } else {
-                            None
-                        }
-                    })
-                    .collect::<Vec<_>>();
-                output.show_json(&h)?;
-            }
-        }
-        Ok(())
-    }
-}
-
-/// What to show in a system file.
-#[derive(Clone, Copy, Debug, Default, PartialEq, ValueEnum)]
-enum Mode {
-    /// File dictionary, with variables, value labels, ...
-    #[default]
-    #[value(alias = "dict")]
-    Dictionary,
-
-    /// File metadata not included in the dictionary.
-    Metadata,
-
-    /// Histogram of character incidence in the file.
-    Histogram,
-}
-
-impl Mode {
-    fn as_str(&self) -> &'static str {
-        match self {
-            Mode::Dictionary => "dictionary",
-            Mode::Metadata => "metadata",
-            Mode::Histogram => "histogram",
-        }
-    }
-}
-
-impl Display for Mode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.as_str())
-    }
-}
diff --git a/rust/pspp/src/cli/show_spv.rs b/rust/pspp/src/cli/show_spv.rs
deleted file mode 100644 (file)
index d852081..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-// PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
-//
-// This program is free software: you can redistribute it and/or modify it under
-// the terms of the GNU General Public License as published by the Free Software
-// Foundation, either version 3 of the License, or (at your option) any later
-// version.
-//
-// This program is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
-// details.
-//
-// You should have received a copy of the GNU General Public License along with
-// this program.  If not, see <http://www.gnu.org/licenses/>.
-
-use anyhow::Result;
-use binrw::{BinRead, error::ContextExt};
-use clap::{Args, ValueEnum};
-use pspp::{
-    output::{
-        Criteria, Item, Itemlike, SpvMembers,
-        pivot::{Axis3, Dimension, Group, Leaf, PivotTable, value::Value},
-    },
-    spv::{
-        SpvArchive,
-        legacy_bin::LegacyBin,
-        read::{
-            ReadSeek,
-            legacy_xml::{Visualization, datum_as_format},
-            structure::OutlineItem,
-        },
-    },
-};
-use std::{
-    fmt::Display,
-    io::{BufReader, Cursor, Read},
-    path::PathBuf,
-    sync::Arc,
-};
-
-/// Show information about SPSS viewer files (SPV files).
-#[derive(Args, Clone, Debug)]
-pub struct ShowSpv {
-    /// What to show.
-    #[arg(value_enum)]
-    mode: Mode,
-
-    /// File to show.
-    ///
-    /// For most modes, this should be a `.spv` file.  For `convert-table-look`,
-    /// this should be a `.tlo` or `.stt` file.
-    #[arg(required = true)]
-    input: PathBuf,
-
-    /// Password for decryption.
-    ///
-    /// In addition to file encryption, SPSS supports a feature called "password
-    /// encryption".  The password specified can be specified with or without
-    /// "password encryption".
-    ///
-    /// Specify only for an encrypted SPV file.
-    #[clap(short, long)]
-    password: Option<String>,
-
-    /// Input selection options.
-    #[command(flatten)]
-    criteria: Criteria,
-
-    /// Include ZIP member names in `dir` output.
-    #[arg(long = "member-names")]
-    show_member_names: bool,
-}
-
-/// What to show in a viewer file.
-#[derive(Clone, Copy, Debug, PartialEq, ValueEnum)]
-enum Mode {
-    /// List tables and other items.
-    #[value(alias = "dir")]
-    Directory,
-
-    /// Copies first selected TableLook into output in `.stt` format.
-    GetTableLook,
-
-    /// Reads `.tlo` or `.stt` TableLook and outputs as `.stt` format.
-    ConvertTableLook,
-
-    /// Print data values in legacy tables.
-    ///
-    /// Data values come from `_tableData.bin` members inside the SPV files.
-    /// They do not require reading the corresponding `_table.xml` files.
-    LegacyData,
-
-    /// Print data series in legacy tables.
-    ///
-    /// The series come from `_tableData.bin` members inside the SPV files, as
-    /// transformed by instructions in their paired `_table.xml` files.
-    LegacySeries,
-
-    /// Prints contents.
-    View,
-}
-
-impl Mode {
-    fn as_str(&self) -> &'static str {
-        match self {
-            Mode::Directory => "directory",
-            Mode::GetTableLook => "get-table-look",
-            Mode::ConvertTableLook => "convert-table-look",
-            Mode::LegacyData => "legacy-data",
-            Mode::LegacySeries => "legacy-series",
-            Mode::View => "view",
-        }
-    }
-}
-
-impl Display for Mode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.as_str())
-    }
-}
-
-impl ShowSpv {
-    pub fn run(self) -> Result<()> {
-        match self.mode {
-            Mode::Directory => self.directory(),
-            Mode::View => self.view(),
-            Mode::LegacyData => self.legacy_data(),
-            Mode::LegacySeries => self.legacy_series(),
-            Mode::GetTableLook => todo!(),
-            Mode::ConvertTableLook => todo!(),
-        }
-    }
-
-    fn read(&self) -> Result<Vec<Arc<Item>>> {
-        Ok(self.criteria.apply(
-            SpvArchive::open_file(&self.input, self.password.as_deref())?
-                .read(|e| eprintln!("{e}"))?
-                .into_items(),
-        ))
-    }
-
-    fn read_outline(&self) -> Result<(SpvArchive<Box<dyn ReadSeek>>, Vec<Arc<OutlineItem>>)> {
-        let mut archive = SpvArchive::open_file(&self.input, self.password.as_deref())?;
-        let outline = archive.read_outline(|w| eprintln!("{w}"))?;
-        Ok((archive, self.criteria.apply(outline.items)))
-    }
-
-    fn directory(self) -> Result<()> {
-        for child in self.read_outline()?.1 {
-            print_item_directory(&*child, 0, self.show_member_names);
-        }
-        Ok(())
-    }
-
-    fn view(self) -> Result<()> {
-        for child in self.read()? {
-            println!("{child}");
-        }
-        Ok(())
-    }
-
-    fn legacy_data(self) -> Result<()> {
-        let (mut archive, items) = self.read_outline()?;
-        for item in items {
-            for item in item.iter_in_order() {
-                if let Some(spv_info) = item.spv_info()
-                    && let Some(members) = &spv_info.members
-                    && let Some(binary) = members.bin_member()
-                {
-                    let mut bin_member = archive.0.by_name(binary)?;
-                    let mut bin_data = Vec::with_capacity(bin_member.size() as usize);
-                    bin_member.read_to_end(&mut bin_data)?;
-                    let mut cursor = Cursor::new(bin_data);
-                    let legacy_bin = LegacyBin::read(&mut cursor).map_err(|e| {
-                        e.with_message(format!(
-                            "While parsing {binary:?} as legacy binary SPV member"
-                        ))
-                    })?;
-                    let data = legacy_bin.decode(&mut |w| eprintln!("{w}"));
-                    let n_values = data
-                        .values()
-                        .flat_map(|map| map.values())
-                        .map(|values| values.len())
-                        .max()
-                        .unwrap_or(0);
-                    let index = Dimension::new(
-                        Group::new("Index")
-                            .with_multiple(Leaf::numbers(0..n_values))
-                            .with_label_shown(),
-                    );
-                    let variables = Dimension::new(Group::new("Variables").with_multiple(
-                        data.iter().map(|(name, contents)| {
-                            Group::new(name.as_str()).with_multiple(contents.keys().map(|name| {
-                                name.replace("categories", "\ncategories")
-                                    .replace("labels", "\nlabels")
-                                    .replace("group", "\ngroup")
-                                    .replace("Label", "\nLabel")
-                            }))
-                        }),
-                    ));
-                    let mut pivot_table =
-                        PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]);
-                    for (variable_index, (variable_name, values)) in
-                        data.values().flat_map(|map| map.iter()).enumerate()
-                    {
-                        for (value_index, data_value) in values.iter().enumerate() {
-                            let value = Value::new_datum(data_value).with_value_label(
-                                (variable_name == "cellFormat")
-                                    .then(|| datum_as_format(data_value).to_string()),
-                            );
-                            pivot_table.insert([value_index, variable_index], value);
-                        }
-                    }
-                    println!("{pivot_table}");
-                }
-            }
-        }
-        Ok(())
-    }
-
-    fn legacy_series(self) -> Result<()> {
-        let (mut archive, items) = self.read_outline()?;
-        for item in items {
-            for item in item.iter_in_order() {
-                if let Some(spv_info) = item.spv_info()
-                    && let Some(members) = &spv_info.members
-                    && let SpvMembers::LegacyTable { xml, binary } = &members
-                {
-                    // Read and decode binary file.
-                    let mut bin_member = archive.0.by_name(&binary)?;
-                    let mut bin_data = Vec::with_capacity(bin_member.size() as usize);
-                    bin_member.read_to_end(&mut bin_data)?;
-                    let mut cursor = Cursor::new(bin_data);
-                    let legacy_bin = LegacyBin::read(&mut cursor).map_err(|e| {
-                        e.with_message(format!(
-                            "While parsing {binary:?} as legacy binary SPV member"
-                        ))
-                    })?;
-                    let data = legacy_bin.decode(&mut |w| eprintln!("{w}"));
-                    drop(bin_member);
-
-                    // Read and decode series in XML file.
-                    let member = BufReader::new(archive.0.by_name(&xml)?);
-                    let visualization: Visualization = match serde_path_to_error::deserialize(
-                        &mut quick_xml::de::Deserializer::from_reader(member),
-                    ) {
-                        Ok(result) => result,
-                        Err(error) => panic!("{error:?}"),
-                    };
-                    let series = visualization.decode_series(data, &mut |w| {
-                        eprintln!("{w}");
-                    });
-
-                    let n_values = series
-                        .values()
-                        .map(|map| map.values.len())
-                        .max()
-                        .unwrap_or(0);
-                    let index = Dimension::new(
-                        Group::new("Index")
-                            .with_multiple(Leaf::numbers(0..n_values))
-                            .with_label_shown(),
-                    );
-                    let variables = Dimension::new(Group::new("Series").with_multiple(
-                        series.values().map(|series| {
-                            series
-                                .name
-                                .replace("categories", "\ncategories")
-                                .replace("labels", "\nlabels")
-                                .replace("group", "\ngroup")
-                                .replace("Label", "\nLabel")
-                        }),
-                    ));
-                    let mut pivot_table =
-                        PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]);
-                    for (series_index, series) in series.values().enumerate() {
-                        for (value_index, data_value) in series.values.iter().enumerate() {
-                            pivot_table
-                                .insert([value_index, series_index], Value::new_datum(data_value));
-                        }
-                    }
-                    println!("{pivot_table}");
-                }
-            }
-        }
-        Ok(())
-    }
-}
-
-fn print_item_directory<T>(item: &T, level: usize, show_member_names: bool)
-where
-    T: Itemlike,
-{
-    for _ in 0..level {
-        print!("    ");
-    }
-    print!("- {} {:?}", item.kind(), item.label());
-    /*
-    if let Some(table) = item.details.as_table() {
-        let title = table.title().display(table).to_string();
-        if item.label.as_ref().is_none_or(|label| label != &title) {
-            print!(" title {title:?}");
-        }
-    }*/
-    if let Some(command_name) = item.command_name() {
-        print!(" command {command_name:?}");
-    }
-    if let Some(subtype) = item.subtype()
-        && let label = item.label().as_ref()
-        && label != &subtype
-    {
-        print!(" subtype {subtype:?}");
-    }
-    if item.is_expanded() == Some(false) {
-        print!(" (collapsed");
-    }
-    if item.is_shown() == Some(false) {
-        print!(" (hidden)");
-    }
-    if show_member_names && let Some(spv_info) = item.spv_info() {
-        for (index, name) in spv_info.member_names().into_iter().enumerate() {
-            print!(" {} {name:?}", if index == 0 { "in" } else { "and" });
-        }
-    }
-    println!();
-    for child in item.children() {
-        print_item_directory(&**child, level + 1, show_member_names);
-    }
-}
diff --git a/rust/pspp/src/cli/spv.rs b/rust/pspp/src/cli/spv.rs
new file mode 100644 (file)
index 0000000..b9df03f
--- /dev/null
@@ -0,0 +1,320 @@
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+use anyhow::{Result, anyhow};
+use clap::{Args, Subcommand};
+use pspp::{
+    file::{FileType, ReadSeek},
+    output::{
+        Criteria, Itemlike, SpvMembers,
+        pivot::{Axis3, Dimension, Group, Leaf, PivotTable, value::Value},
+    },
+    spv::{
+        SpvArchive,
+        legacy_bin::LegacyBin,
+        read::{
+            legacy_xml::Visualization,
+            structure::{Content, OutlineItem},
+        },
+    },
+};
+use std::{io::BufReader, path::PathBuf};
+
+/// Examine SPSS viewer files (SPV files).
+#[derive(Subcommand, Clone, Debug)]
+pub enum Spv {
+    #[command(alias = "dir")]
+    Directory(Directory),
+    /// Copies first selected TableLook into output in `.stt` format.
+    GetTableLook,
+    /// Reads `.tlo` or `.stt` TableLook and outputs as `.stt` format.
+    ConvertTableLook,
+    LegacyData(LegacyData),
+    LegacySeries(LegacySeries),
+    ExportCharts(ExportCharts),
+}
+
+impl Spv {
+    pub fn run(self) -> Result<()> {
+        match self {
+            Spv::Directory(directory) => directory.run(),
+            Spv::GetTableLook => todo!(),
+            Spv::ConvertTableLook => todo!(),
+            Spv::LegacyData(legacy_data) => legacy_data.run(),
+            Spv::LegacySeries(legacy_series) => legacy_series.run(),
+            Spv::ExportCharts(export_charts) => export_charts.run(),
+        }
+    }
+}
+
+#[derive(Args, Clone, Debug)]
+struct SpvInput {
+    /// SPV file to read.
+    #[arg(required = true)]
+    input: PathBuf,
+
+    /// Password for decryption, with or without what SPSS calls "password encryption".
+    ///
+    /// Specify only for an encrypted SPV file.
+    #[clap(short, long)]
+    password: Option<String>,
+
+    /// Input selection options.
+    #[command(flatten)]
+    criteria: Criteria,
+}
+
+impl SpvInput {
+    fn read_outline(&self) -> Result<(SpvArchive<Box<dyn ReadSeek>>, Vec<OutlineItem>)> {
+        let mut archive = SpvArchive::open_file(&self.input, self.password.as_deref())?;
+        let items = archive
+            .read_outline(|w| eprintln!("{w}"))?
+            .filter(&self.criteria)
+            .into_items();
+        Ok((archive, items))
+    }
+}
+
+/// List tables and other items in an SPV file.
+#[derive(Args, Clone, Debug)]
+pub struct Directory {
+    /// SPV input file.
+    #[command(flatten)]
+    input: SpvInput,
+
+    /// Include ZIP member names in output.
+    #[arg(long = "member-names")]
+    #[arg(help_heading = "Output")]
+    show_member_names: bool,
+}
+
+impl Directory {
+    fn run(self) -> Result<()> {
+        for child in self.input.read_outline()?.1 {
+            print_item_directory(&child, 0, self.show_member_names);
+        }
+        Ok(())
+    }
+}
+
+/// Print data values in legacy tables.
+///
+/// Data values come from `_tableData.bin` members inside the SPV files.
+/// They do not require reading the corresponding `_table.xml` files.
+#[derive(Args, Clone, Debug)]
+pub struct LegacyData {
+    /// SPV input file.
+    #[command(flatten)]
+    input: SpvInput,
+}
+
+impl LegacyData {
+    fn run(self) -> Result<()> {
+        if let Some(FileType::LegacyBin) = FileType::from_file(&self.input.input)? {
+            let data = std::fs::read(&self.input.input)?;
+            let data = LegacyBin::from_data(&data, &mut |w| eprintln!("{w}"))?;
+            println!("{}", data.as_pivot_table());
+        } else {
+            let (mut archive, items) = self.input.read_outline()?;
+            for item in items {
+                for item in item.iter_preorder() {
+                    if let Some(spv_info) = item.spv_info()
+                        && let Some(members) = &spv_info.members
+                        && let Some(binary) = members.bin_member()
+                    {
+                        let data = archive
+                            .read_legacy_bin(binary, &mut |w| eprintln!("{w}"))
+                            .map_err(|error| {
+                                anyhow::Error::from(error).context(format!(
+                                    "While parsing {binary:?} as legacy binary SPV member"
+                                ))
+                            })?;
+
+                        println!("{}", data.as_pivot_table());
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Print data series in legacy tables.
+///
+/// The series come from `_tableData.bin` members inside the SPV files, as
+/// transformed by instructions in their paired `_table.xml` files.
+#[derive(Args, Clone, Debug)]
+pub struct LegacySeries {
+    /// SPV input file.
+    #[command(flatten)]
+    input: SpvInput,
+}
+
+impl LegacySeries {
+    fn run(self) -> Result<()> {
+        let (mut archive, items) = self.input.read_outline()?;
+        for item in items {
+            for item in item.iter_preorder() {
+                if let Some(spv_info) = item.spv_info()
+                    && let Some(members) = &spv_info.members
+                    && let SpvMembers::LegacyTable { xml, binary } = &members
+                {
+                    let data = archive
+                        .read_legacy_bin(binary, &mut |w| eprintln!("{w}"))
+                        .map_err(|error| {
+                            anyhow::Error::from(error).context(format!(
+                                "While parsing {binary:?} as legacy binary SPV member"
+                            ))
+                        })?;
+
+                    // Read and decode series in XML file.
+                    let member = BufReader::new(archive.0.by_name(xml)?);
+                    let visualization: Visualization = match serde_path_to_error::deserialize(
+                        &mut quick_xml::de::Deserializer::from_reader(member),
+                    ) {
+                        Ok(result) => result,
+                        Err(error) => panic!("{error:?}"),
+                    };
+                    let series = visualization.decode_series(data, &mut |w| {
+                        eprintln!("{w}");
+                    });
+
+                    let n_values = series
+                        .values()
+                        .map(|map| map.values.len())
+                        .max()
+                        .unwrap_or(0);
+                    let index = Dimension::new(
+                        Group::new("Index")
+                            .with_multiple(Leaf::numbers(0..n_values))
+                            .with_label_shown(),
+                    );
+                    let variables = Dimension::new(Group::new("Series").with_multiple(
+                        series.values().map(|series| {
+                            series
+                                .name
+                                .replace("categories", "\ncategories")
+                                .replace("labels", "\nlabels")
+                                .replace("group", "\ngroup")
+                                .replace("Label", "\nLabel")
+                        }),
+                    ));
+                    let mut pivot_table =
+                        PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]);
+                    for (series_index, series) in series.values().enumerate() {
+                        for (value_index, data_value) in series.values.iter().enumerate() {
+                            pivot_table
+                                .insert([value_index, series_index], Value::new_datum(data_value));
+                        }
+                    }
+                    println!("{pivot_table}");
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Writes a standalone XML file for each chart in the input.
+///
+/// The output file name should end in `.xml`.
+#[derive(Args, Clone, Debug)]
+pub struct ExportCharts {
+    /// SPV input file.
+    #[command(flatten)]
+    input: SpvInput,
+
+    /// Output file name.
+    #[arg(help_heading = "Arguments")]
+    output: PathBuf,
+}
+
+impl ExportCharts {
+    fn run(self) -> Result<()> {
+        let (mut archive, items) = self.input.read_outline()?;
+        let mut output = self.output.clone();
+        let extension = output.extension().map(|extension| extension.to_os_string());
+        output.set_extension("");
+        let Some(file_name) = output.file_name().map(|file_name| file_name.to_os_string()) else {
+            return Err(anyhow!("invalid export-charts output file name"));
+        };
+        let mut index = 1;
+        for item in items {
+            for item in item.iter_preorder() {
+                if let OutlineItem::Container(container) = item
+                    && let Content::Graph(graph) = container.content()
+                {
+                    let xml = graph
+                        .decode(&mut archive)?
+                        .to_chart_xml(|w| eprintln!("{w}"))?;
+                    let mut file_name = file_name.clone();
+                    if index > 1 {
+                        file_name.push(format!("-{index}"))
+                    };
+                    index += 1;
+                    let mut output = output.clone();
+                    output.set_file_name(file_name);
+                    if let Some(extension) = &extension {
+                        output.add_extension(extension);
+                    }
+                    std::fs::write(&output, &xml)?;
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+fn print_item_directory<T>(item: &T, level: usize, show_member_names: bool)
+where
+    T: Itemlike,
+{
+    for _ in 0..level {
+        print!("    ");
+    }
+    print!("- {} {:?}", item.kind(), item.label());
+    /*
+    if let Some(table) = item.details.as_table() {
+        let title = table.title().display(table).to_string();
+        if item.label.as_ref().is_none_or(|label| label != &title) {
+            print!(" title {title:?}");
+        }
+    }*/
+    if let Some(command_name) = item.command_name() {
+        print!(" command {command_name:?}");
+    }
+    if let Some(subtype) = item.subtype()
+        && let label = item.label().as_ref()
+        && label != subtype
+    {
+        print!(" subtype {subtype:?}");
+    }
+    if item.is_expanded() == Some(false) {
+        print!(" (collapsed");
+    }
+    if item.is_shown() == Some(false) {
+        print!(" (hidden)");
+    }
+    if show_member_names && let Some(spv_info) = item.spv_info() {
+        for (index, name) in spv_info.member_names().into_iter().enumerate() {
+            print!(" {} {name:?}", if index == 0 { "in" } else { "and" });
+        }
+    }
+    println!();
+    for child in item.children() {
+        print_item_directory(&**child, level + 1, show_member_names);
+    }
+}
index 78f88f708b8422ebecc9d73da75af0342705e8bb..f4c0f21369f9beccc511c2490f79e26525eee48a 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -622,10 +622,10 @@ fn parse_token_if<F, R>(input: &TokenSlice, parse: F) -> ParseResult<R>
 where
     F: Fn(&Token) -> Option<R>,
 {
-    if let Some(token) = input.get_token(0) {
-        if let Some(result) = parse(token) {
-            return Ok(Parsed::ok(result, input.subslice(1..input.len())));
-        }
+    if let Some(token) = input.get_token(0)
+        && let Some(result) = parse(token)
+    {
+        return Ok(Parsed::ok(result, input.subslice(1..input.len())));
     }
     Err(ParseError::Mismatch(Diagnostics::default()))
 }
@@ -719,10 +719,10 @@ impl FromTokens for VarRange {
             Ok(Parsed::ok(Self::All, rest))
         } else {
             let (from, rest, _) = parse_id(input)?.into_tuple();
-            if let Ok(Parsed { rest, .. }) = parse_token(&rest, &Token::Punct(Punct::To)) {
-                if let Ok(p) = parse_id(&rest) {
-                    return Ok(p.map(|to| Self::Range(from, to)));
-                }
+            if let Ok(Parsed { rest, .. }) = parse_token(&rest, &Token::Punct(Punct::To))
+                && let Ok(p) = parse_id(&rest)
+            {
+                return Ok(p.map(|to| Self::Range(from, to)));
             }
             Ok(Parsed::ok(Self::Single(from), rest))
         }
@@ -750,10 +750,9 @@ fn parse_format(input: &TokenSlice) -> ParseResult<AbstractFormat> {
         token: Token::Id(id),
         ..
     }) = iter.next()
+        && let Ok(format) = id.0.as_ref().parse()
     {
-        if let Ok(format) = id.0.as_ref().parse() {
-            return Ok(Parsed::ok(format, iter.remainder()));
-        }
+        return Ok(Parsed::ok(format, iter.remainder()));
     }
     Err(ParseError::Mismatch(
         input.error("Syntax error expecting identifier.").into(),
index 53344af1493cd64069d90a5b1e067695e84a65e8..17e9aad73a59bab8374b5c59923d9106368adb10 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 2556bdd8cc98d5c7357898613835fe6a4c839bec..4fbe57c26c36b442c2aa3e5b87bf72930c282b88 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index c5becd3657178ace6341ea3f49402fe5ef609a86..2141fba4d5c4e3f385869ef08a58e41cb53b6558 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 656274dffab301ae5c816c00a18415e716a053be..3ee36cbf7177b678511a60f71d2a3a435a1c1162 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index ac7a2c4aecd26b1803a168a920ad5e5ae92e10a7..d4582d311b0c899dd782397177277e24c751fbb1 100644 (file)
@@ -16,17 +16,43 @@ use aes::{
     cipher::{BlockDecrypt, KeyInit, generic_array::GenericArray},
 };
 use cmac::{Cmac, Mac};
-use displaydoc::Display;
 use smallvec::SmallVec;
 use std::{
-    fmt::Debug,
-    io::{BufRead, Error as IoError, ErrorKind, Read, Seek, SeekFrom},
+    fmt::{Debug, Display},
+    io::{BufRead, BufReader, Error as IoError, ErrorKind, Read, Seek, SeekFrom},
 };
 
-use binrw::{BinRead, io::NoSeek};
+use binrw::BinRead;
+
+use crate::file::{BufReadSeek, ReadSeek};
+
+/// Error opening an encrypted file.
+pub struct OpenError<R> {
+    /// The reader used to open the file.
+    ///
+    /// The file position might have been moved.
+    pub reader: R,
+
+    /// The details of the error.
+    pub details: Error,
+}
+
+impl<R> Debug for OpenError<R> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", &self.details)
+    }
+}
+
+impl<R> Display for OpenError<R> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", &self.details)
+    }
+}
+
+impl<R> std::error::Error for OpenError<R> {}
 
 /// Error reading an encrypted file.
-#[derive(Clone, Debug, thiserror::Error, Display)]
+#[derive(Clone, Debug, thiserror::Error, displaydoc::Display)]
 pub enum Error {
     /// I/O error reading encrypted file wrapper ({0}).
     IoError(ErrorKind),
@@ -43,8 +69,8 @@ pub enum Error {
     /// Unknown file type {0:?}.
     UnknownFileType(String),
 
-    /// Incorrect password.
-    WrongPassword,
+    /// Correct password required to read encrypted file.
+    NeedPassword,
 }
 
 impl From<std::io::Error> for Error {
@@ -66,6 +92,125 @@ struct EncryptedHeader {
     _ignore2: [u8; 16],
 }
 
+/// A file that might be encrypted.
+///
+/// This is a flexible way to open a file that might or might not be encrypted.
+pub struct MaybeEncryptedFile<R>(MaybeEncryptedInner<R>);
+
+impl<R> MaybeEncryptedFile<R> {
+    /// Constructs a new `MaybeEncryptedFile` from `reader`.
+    ///
+    /// This tries to read an encrypted file header from `reader`.  It will
+    /// report an error only if the I/O fails or if it reads an invalid
+    /// encryption header.
+    pub fn new(reader: R) -> Result<Self, OpenError<R>>
+    where
+        R: Read + Seek,
+    {
+        match EncryptedFile::new(reader) {
+            Ok(file) => Ok(Self(MaybeEncryptedInner::Locked(file))),
+            Err(OpenError {
+                mut reader,
+                details: Error::NotEncrypted,
+            }) => match reader.rewind() {
+                Ok(_) => Ok(Self(MaybeEncryptedInner::Plaintext(reader))),
+                Err(error) => Err(OpenError {
+                    reader,
+                    details: error.into(),
+                }),
+            },
+            Err(error) => Err(error),
+        }
+    }
+
+    /// Returns true if the file is not encrypted or if it is encrypted but it
+    /// has been unlocked by supplying the correct password.
+    pub fn is_open(&self) -> bool {
+        !matches!(&self.0, MaybeEncryptedInner::Locked(_))
+    }
+
+    /// Returns true if the file is encrypted (regardless of whether the correct
+    /// password has been supplied).
+    pub fn is_encrypted(&self) -> bool {
+        !matches!(&self.0, MaybeEncryptedInner::Plaintext(_))
+    }
+
+    /// Attempts to unlock this possibly encrypted file.  If this file is
+    /// encrypted and `password` is the correct password, then this returns the
+    /// unlocked file; otherwise, it returns the same `MaybeEncryptedFile`
+    /// unchanged.
+    ///
+    /// The password itself may be "encrypted" or plaintext.
+    pub fn with_password<P>(self, password: Option<P>) -> Self
+    where
+        R: Read + Seek,
+        P: AsRef<[u8]>,
+    {
+        if let Some(password) = password {
+            match self.0 {
+                MaybeEncryptedInner::Locked(encrypted_file) => {
+                    match encrypted_file.unlock(password) {
+                        Ok(reader) => Self(MaybeEncryptedInner::Unlocked(reader)),
+                        Err(reader) => Self(MaybeEncryptedInner::Locked(reader)),
+                    }
+                }
+                other => Self(other),
+            }
+        } else {
+            self
+        }
+    }
+
+    /// If this file is plaintext or its password has been supplied, returns a
+    /// [ReadSeek] trait object.  Otherwise, returns an error indicating that
+    /// the file is encrypted but the password is unknown.
+    pub fn open(self) -> Result<Box<dyn ReadSeek>, OpenError<R>>
+    where
+        R: Read + Seek + 'static,
+    {
+        match self.0 {
+            MaybeEncryptedInner::Plaintext(reader) => Ok(Box::new(reader)),
+            MaybeEncryptedInner::Unlocked(reader) => Ok(Box::new(reader)),
+            MaybeEncryptedInner::Locked(reader) => Err(OpenError {
+                reader: reader.into_reader(),
+                details: Error::NeedPassword,
+            }),
+        }
+    }
+
+    /// If this file is plaintext or its password has been supplied, returns a
+    /// [BufReadSeek] trait object.  Otherwise, returns an error indicating that
+    /// the file is encrypted but the password is unknown.
+    pub fn open_buffered(self) -> Result<Box<dyn BufReadSeek>, OpenError<R>>
+    where
+        R: Read + Seek + 'static,
+    {
+        match self.0 {
+            MaybeEncryptedInner::Plaintext(reader) => Ok(Box::new(BufReader::new(reader))),
+            MaybeEncryptedInner::Unlocked(reader) => Ok(Box::new(reader)),
+            MaybeEncryptedInner::Locked(reader) => Err(OpenError {
+                reader: reader.into_reader(),
+                details: Error::NeedPassword,
+            }),
+        }
+    }
+
+    /// Returns the original `reader` passed to [MaybeEncryptedFile::new].
+    pub fn into_reader(self) -> R {
+        match self.0 {
+            MaybeEncryptedInner::Plaintext(reader) => reader,
+            MaybeEncryptedInner::Locked(reader) => reader.into_reader(),
+            MaybeEncryptedInner::Unlocked(reader) => reader.into_reader(),
+        }
+    }
+}
+
+enum MaybeEncryptedInner<R> {
+    Plaintext(R),
+    Locked(EncryptedFile<R>),
+    Unlocked(EncryptedReader<R>),
+}
+
 /// An encrypted file.
 #[derive(Clone)]
 pub struct EncryptedFile<R> {
@@ -113,34 +258,43 @@ where
     ///
     /// [unlock]: Self::unlock
     /// [unlock_literal]: Self::unlock_literal
-    pub fn new(mut reader: R) -> Result<Self, Error> {
-        let header =
-            EncryptedHeader::read_le(&mut NoSeek::new(&mut reader)).map_err(
-                |error| match error {
-                    binrw::Error::BadMagic { .. } => Error::NotEncrypted,
-                    binrw::Error::Io(error) => Error::IoError(error.kind()),
-                    _ => unreachable!(),
-                },
-            )?;
-        let file_type = match &header.file_type {
-            b"SAV" => FileType::Data,
-            b"SPV" => FileType::Viewer,
-            b"SPS" => FileType::Syntax,
-            _ => {
-                return Err(Error::UnknownFileType(
-                    header.file_type.iter().map(|b| *b as char).collect(),
-                ));
+    pub fn new(mut reader: R) -> Result<Self, OpenError<R>> {
+        fn inner<R>(reader: &mut R) -> Result<(FileType, u64, [u8; 16], [u8; 16]), Error>
+        where
+            R: Read + Seek,
+        {
+            let header = EncryptedHeader::read_le(&mut *reader).map_err(|error| match error {
+                binrw::Error::BadMagic { .. } => Error::NotEncrypted,
+                _ if error.is_eof() => Error::NotEncrypted,
+                binrw::Error::Io(error) => Error::IoError(error.kind()),
+                _ => unreachable!("{error:?}"),
+            })?;
+            let file_type = match &header.file_type {
+                b"SAV" => FileType::Data,
+                b"SPV" => FileType::Viewer,
+                b"SPS" => FileType::Syntax,
+                _ => {
+                    return Err(Error::UnknownFileType(
+                        header.file_type.iter().map(|b| *b as char).collect(),
+                    ));
+                }
+            };
+            let mut first_block = [0; 16];
+            reader.read_exact(&mut first_block)?;
+            let length = reader.seek(SeekFrom::End(-16))? + 16;
+            if length < 36 + 16 || (length - 36) % 16 != 0 {
+                return Err(Error::InvalidLength(length + 36));
             }
-        };
-        let mut first_block = [0; 16];
-        reader.read_exact(&mut first_block)?;
-        let length = reader.seek(SeekFrom::End(-16))? + 16;
-        if length < 36 + 16 || (length - 36) % 16 != 0 {
-            return Err(Error::InvalidLength(length + 36));
+            let mut last_block = [0; 16];
+            reader.read_exact(&mut last_block)?;
+            reader.seek(SeekFrom::Start(36))?;
+            Ok((file_type, length, first_block, last_block))
         }
-        let mut last_block = [0; 16];
-        reader.read_exact(&mut last_block)?;
-        reader.seek(SeekFrom::Start(36))?;
+
+        let (file_type, length, first_block, last_block) = match inner(&mut reader) {
+            Ok(reader) => reader,
+            Err(details) => return Err(OpenError { reader, details }),
+        };
         Ok(Self {
             reader,
             file_type,
@@ -161,7 +315,7 @@ where
         let password = password.as_ref();
         self.unlock_literal(password).or_else(|this| {
             match EncodedPassword::from_encoded(password) {
-                Some(encoded) => this.unlock_literal(&encoded.decode()),
+                Some(encoded) => this.unlock_literal(encoded.decode()),
                 None => Err(this),
             }
         })
@@ -207,7 +361,7 @@ where
         let password = password.get(..10).unwrap_or(password);
         let n = password.len();
 
-        //  padded_password = password padded with zeros to 32 bytes.
+        // padded_password = password padded with zeros to 32 bytes.
         let mut padded_password = [0; 32];
         padded_password[..n].copy_from_slice(password);
 
@@ -256,11 +410,18 @@ where
             self.length - 36 - padding_length as u64,
         ))
     }
+}
 
+impl<R> EncryptedFile<R> {
     /// Returns the type of encrypted file.
     pub fn file_type(&self) -> FileType {
         self.file_type
     }
+
+    /// Consumes the encrypted file, returning the reader.
+    pub fn into_reader(self) -> R {
+        self.reader
+    }
 }
 
 fn parse_padding(block: &[u8; 16]) -> Option<usize> {
@@ -308,24 +469,24 @@ pub struct EncryptedReader<R> {
     tail: usize,
 }
 
-/// The [Read] and [Seek] traits together, for use as `dyn ReadSeek`.
-pub trait ReadSeek: Read + Seek {}
-impl<T> ReadSeek for T where T: Read + Seek {}
-
 impl<R> EncryptedReader<R> {
     /// Opens `reader` and unlocks it with the given password in one step.
     ///
     /// This fails if the password is wrong.  To allow for multiple password
     /// tries, use [EncryptedFile::new] followed by [EncryptedFile::unlock]
     /// instead.
-    pub fn open<P>(reader: R, password: P) -> Result<Self, Error>
+    pub fn open<P>(reader: R, password: P) -> Result<Self, OpenError<R>>
     where
         R: Read + Seek,
         P: AsRef<[u8]>,
     {
-        EncryptedFile::new(reader)?
-            .unlock(password)
-            .map_err(|_| Error::WrongPassword)
+        match EncryptedFile::new(reader)?.unlock(password) {
+            Ok(reader) => Ok(reader),
+            Err(file) => Err(OpenError {
+                reader: file.into_reader(),
+                details: Error::NeedPassword,
+            }),
+        }
     }
 
     fn new(reader: R, aes: Aes256Dec, file_type: FileType, length: u64) -> Self {
@@ -352,6 +513,11 @@ impl<R> EncryptedReader<R> {
     pub fn file_type(&self) -> FileType {
         self.file_type
     }
+
+    /// Consumes the reader, returning the inner reader supplied to `open`.
+    pub fn into_reader(self) -> R {
+        self.reader
+    }
 }
 
 impl<R> EncryptedReader<R>
@@ -661,6 +827,16 @@ mod tests {
         );
     }
 
+    #[test]
+    fn short_file() {
+        assert!(matches!(
+            EncryptedFile::new(Cursor::new(b"abcd"))
+                .unwrap_err()
+                .details,
+            super::Error::NotEncrypted
+        ));
+    }
+
     #[test]
     fn password_encoding() {
         // Decode a few specific passwords.
index 799d4ebf6f8c797f862ec9e633e2a2b693c3dd8c..f71fd3c5e8a7fa5fb68de9eb3be9659557474da4 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -930,7 +930,7 @@ where
         ]);
         for (row_number, row) in data.into_iter().enumerate() {
             for (column_number, datum) in row.into_iter().enumerate() {
-                pt.insert(&[column_number, row_number], datum);
+                pt.insert([column_number, row_number], datum);
             }
         }
         output.push(pt.into());
index a76d85e944585ae676632f0069e28ccfd1185c7c..076e3448e437176e83f0f7df0865940137254536 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index c65e695b35f0f7531df3ea0f56c93f38b400af52..9190b7bcfe3af9ad0bc93be8dbde54bb45685549 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -733,7 +733,7 @@ impl<'a> OutputVariables<'a> {
         for (var_index, variable) in self.dictionary.variables.iter().enumerate() {
             for (field, field_index) in &columns {
                 if let Some(value) = Self::get_field_value(var_index, variable, *field) {
-                    pt.insert(&[var_index, *field_index], value);
+                    pt.insert([var_index, *field_index], value);
                 }
             }
         }
@@ -798,7 +798,7 @@ impl<'a> OutputValueLabels<'a> {
             let mut sorted_value_labels = variable.value_labels.0.iter().collect::<Vec<_>>();
             sorted_value_labels.sort();
             for (datum, label) in sorted_value_labels {
-                let mut value = Value::new_datum_from_variable(datum, &variable)
+                let mut value = Value::new_datum_from_variable(datum, variable)
                     .with_show_value_label(Some(Show::Value));
                 if variable
                     .missing_values()
@@ -809,7 +809,7 @@ impl<'a> OutputValueLabels<'a> {
                 group.push(value);
 
                 data.push(
-                    Value::new_datum_from_variable(datum, &variable)
+                    Value::new_datum_from_variable(datum, variable)
                         .with_show_value_label(Some(Show::Label))
                         .with_value_label(Some(escape_value_label(label.as_str()).into())),
                 );
@@ -937,12 +937,12 @@ impl<'a> OutputMrsets<'a> {
         ])
         .with_title("Multiple Response Sets");
         for (row, mrset) in self.dictionary.mrsets.iter().enumerate() {
-            pt.insert(&[row, 0], mrset.label.as_str());
+            pt.insert([row, 0], mrset.label.as_str());
 
             let mr_type_name = match &mrset.mr_type {
                 MultipleResponseType::MultipleDichotomy { datum, .. } => {
                     pt.insert(
-                        &[row, 2],
+                        [row, 2],
                         Value::new_datum(&datum.as_encoded(self.dictionary.encoding)),
                     );
                     "Dichotomies"
@@ -950,9 +950,9 @@ impl<'a> OutputMrsets<'a> {
                 MultipleResponseType::MultipleCategory => "Categories",
             };
 
-            pt.insert(&[row, 1], Value::new_text(mr_type_name));
+            pt.insert([row, 1], Value::new_text(mr_type_name));
             pt.insert(
-                &[row, 3],
+                [row, 3],
                 mrset
                     .variables
                     .iter()
@@ -1031,7 +1031,7 @@ impl<'a> OutputAttributes<'a> {
         ])
         .with_title("Data File and Variable Attributes");
         for (row, datum) in data.into_iter().enumerate() {
-            pt.insert(&[0, row], datum);
+            pt.insert([0, row], datum);
         }
         Some(pt)
     }
index ebb2694dc297fd668361405afd6b3319e2879c78..70b94a080b7ba02b5352fb6074e0ca0ae0eab5d2 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index e8a71266e01ec836924aaa00053ed1dce65e9b5d..45cf2e95bac01e26c0f76328109d095ac4f40b50 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index d4d077076bfcd591a92243601b9de632776a00fd..2955b455a7c11a5252251cce78f34ec6425a3f1a 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 
 #![cfg_attr(not(test), warn(missing_docs))]
 use std::{
+    fmt::Display,
     fs::File,
-    io::{Error, Read, Seek},
+    io::{BufRead, Error, Read, Seek},
     path::Path,
 };
 
+use quick_xml::{events::Event, name::QName};
 use zip::ZipArchive;
 
 use crate::sys::raw::Magic;
 
+/// The [Read] and [Seek] traits together, for use as `dyn ReadSeek`.
+pub trait ReadSeek: Read + Seek {}
+impl<T> ReadSeek for T where T: Read + Seek {}
+
+/// The [Read] and [Seek] traits together, for use as `dyn BufReadSeek`.
+pub trait BufReadSeek: BufRead + Seek {}
+impl<T> BufReadSeek for T where T: BufRead + Seek {}
+
 /// Type of a file understood by PSPP.
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum FileType {
@@ -58,6 +68,23 @@ pub enum FileType {
         /// Whether the file is encrypted.
         encrypted: bool,
     },
+
+    /// A "TableLook" file.
+    Look(LookFormat),
+
+    /// A standalone graph exported in XML format.
+    Graph,
+
+    /// A legacy data binary file.
+    ///
+    /// Normally these are seen only inside SPV files.
+    LegacyBin,
+}
+
+impl Display for FileType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.as_str())
+    }
 }
 
 impl FileType {
@@ -104,7 +131,7 @@ impl FileType {
     where
         R: Read + Seek,
     {
-        let mut buf = vec![0; 512];
+        let mut buf = vec![0; 1024];
         let mut n = 0;
         while n < buf.capacity() {
             let count = reader.read(&mut buf[n..])?;
@@ -138,6 +165,31 @@ impl FileType {
             _ => (),
         }
 
+        if buf.get(6..17) == Some(b"PTTableLook") {
+            return Ok(Some(Self::Look(LookFormat::Binary)));
+        }
+
+        if buf.starts_with(b"<") {
+            let mut reader = quick_xml::Reader::from_reader(buf.as_slice());
+            while let Ok(event) = reader.read_event() {
+                match event {
+                    Event::Start(start) if start.name() == QName(b"tableProperties") => {
+                        return Ok(Some(Self::Look(LookFormat::Xml)));
+                    }
+                    Event::Start(start) if start.name() == QName(b"visualization") => {
+                        return Ok(Some(Self::Graph));
+                    }
+                    Event::Text(mut text) => {
+                        if !text.inplace_trim_start() {
+                            break;
+                        }
+                    }
+                    Event::DocType(_) | Event::Decl(_) => (),
+                    _ => break,
+                }
+            }
+        }
+
         if buf
             .get(200 + 256..)
             .unwrap_or_default()
@@ -161,6 +213,10 @@ impl FileType {
             return Ok(Some(Self::Viewer { encrypted: false }));
         }
 
+        if buf.starts_with(&[0, 0xaf]) || buf.starts_with(&[0, 0xb0]) {
+            return Ok(Some(Self::LegacyBin));
+        }
+
         if !buf.is_empty() && !buf.contains(&0) {
             return Ok(Some(Self::Syntax {
                 confident: buf.starts_with(b"* Encoding:"),
@@ -183,13 +239,60 @@ impl FileType {
             FileType::Pc => "pc+",
             FileType::Viewer { .. } => "spv",
             FileType::Syntax { .. } => "sps",
+            FileType::Look(LookFormat::Binary) => "tlo",
+            FileType::Look(LookFormat::Xml) => "stt",
+            FileType::Graph => "xml",
+            FileType::LegacyBin => "bin",
+        }
+    }
+
+    /// Returns a string describing this kind of file.
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            FileType::System { encrypted: false } => "SPSS system",
+            FileType::System { encrypted: true } => "encrypted SPSS system",
+            FileType::Portable => "SPSS portable",
+            FileType::Pc => "SPSS/PC+ system",
+            FileType::Viewer { encrypted: false } => "SPSS output viewer",
+            FileType::Viewer { encrypted: true } => "encrypted SPSS output viewer",
+            FileType::Syntax {
+                confident: true,
+                encrypted: false,
+            } => "SPSS syntax",
+            FileType::Syntax {
+                confident: false,
+                encrypted: false,
+            } => "possible SPSS syntax",
+            FileType::Syntax {
+                confident: _,
+                encrypted: true,
+            } => "encrypted SPSS syntax",
+            FileType::Look(_) => "SPSS TableLook",
+            FileType::Graph => "SPSS exported chart",
+            FileType::LegacyBin => "SPSS legacy binary data",
         }
     }
 }
 
+/// Type of a file that contains a "[TableLook]".
+///
+/// [TableLook]: crate::output::pivot::look::Look
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum LookFormat {
+    /// The `.tlo` binary format for [Look]s.
+    ///
+    /// [Look]: crate::output::pivot::look::Look
+    Binary,
+
+    /// The `.stt` XML format for [Look]s.
+    ///
+    /// [Look]: crate::output::pivot::look::Look
+    Xml,
+}
+
 #[cfg(test)]
 mod tests {
-    use crate::file::FileType;
+    use crate::file::{FileType, LookFormat};
 
     #[test]
     fn file_type() {
@@ -227,5 +330,17 @@ mod tests {
                 encrypted: false
             })
         );
+        assert_eq!(
+            FileType::from_file("src/file/testdata/test.tlo").unwrap(),
+            Some(FileType::Look(LookFormat::Binary))
+        );
+        assert_eq!(
+            FileType::from_file("src/file/testdata/test.stt").unwrap(),
+            Some(FileType::Look(LookFormat::Xml))
+        );
+        assert_eq!(
+            FileType::from_file("src/file/testdata/test.xml").unwrap(),
+            Some(FileType::Graph)
+        );
     }
 }
diff --git a/rust/pspp/src/file/testdata/test.stt b/rust/pspp/src/file/testdata/test.stt
new file mode 100644 (file)
index 0000000..7e16140
--- /dev/null
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<tableProperties xmlns="http://www.ibm.com/software/analytics/spss/xml/table-looks" xmlns:vizml="http://www.ibm.com/software/analytics/spss/xml/visualization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ibm.com/software/analytics/spss/xml/table-looks http://www.ibm.com/software/analytics/spss/xml/table-looks/table-looks-1.4.xsd">
+    <generalProperties hideEmptyRows="true" maximumColumnWidth="72" maximumRowWidth="120" minimumColumnWidth="36" minimumRowWidth="36" rowDimensionLabels="inCorner"/>
+    <footnoteProperties markerPosition="subscript" numberFormat="alphabetic"/>
+    <cellFormatProperties>
+        <title>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="bold" font-underline="none" labelLocationVertical="center" margin-bottom="6pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="left"/>
+        </title>
+        <caption>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="positive" margin-bottom="0pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="left"/>
+        </caption>
+        <footnotes>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="positive" margin-bottom="2pt" margin-left="8pt" margin-right="6pt" margin-top="1pt" textAlignment="left"/>
+        </footnotes>
+        <cornerLabels>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="negative" margin-bottom="0pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="left"/>
+        </cornerLabels>
+        <columnLabels>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="negative" margin-bottom="2pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="center"/>
+        </columnLabels>
+        <rowLabels>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="positive" margin-bottom="2pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="left"/>
+        </rowLabels>
+        <data>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="positive" margin-bottom="0pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="mixed"/>
+        </data>
+        <layers>
+            <vizml:style color="#000000" color2="#ffffff" font-family="Sans Serif" font-size="9pt" font-weight="regular" font-underline="none" labelLocationVertical="negative" margin-bottom="2pt" margin-left="6pt" margin-right="8pt" margin-top="0pt" textAlignment="left"/>
+        </layers>
+    </cellFormatProperties>
+    <borderProperties>
+        <titleLayerSeparator borderStyleType="none" color="#000000"/>
+        <leftOuterFrame borderStyleType="none" color="#000000"/>
+        <topOuterFrame borderStyleType="none" color="#000000"/>
+        <rightOuterFrame borderStyleType="none" color="#000000"/>
+        <bottomOuterFrame borderStyleType="none" color="#000000"/>
+        <leftInnerFrame borderStyleType="thick" color="#000000"/>
+        <topInnerFrame borderStyleType="thick" color="#000000"/>
+        <rightInnerFrame borderStyleType="thick" color="#000000"/>
+        <bottomInnerFrame borderStyleType="thick" color="#000000"/>
+        <dataAreaLeft borderStyleType="thick" color="#000000"/>
+        <dataAreaTop borderStyleType="thick" color="#000000"/>
+        <horizontalDimensionBorderRows borderStyleType="solid" color="#000000"/>
+        <verticalDimensionBorderRows borderStyleType="none" color="#000000"/>
+        <horizontalDimensionBorderColumns borderStyleType="solid" color="#000000"/>
+        <verticalDimensionBorderColumns borderStyleType="solid" color="#000000"/>
+        <horizontalCategoryBorderRows borderStyleType="none" color="#000000"/>
+        <verticalCategoryBorderRows borderStyleType="none" color="#000000"/>
+        <horizontalCategoryBorderColumns borderStyleType="solid" color="#000000"/>
+        <verticalCategoryBorderColumns borderStyleType="solid" color="#000000"/>
+    </borderProperties>
+    <printingProperties printAllLayers="false" rescaleLongTableToFitPage="false" rescaleWideTableToFitPage="false" windowOrphanLines="0"/>
+</tableProperties>
diff --git a/rust/pspp/src/file/testdata/test.tlo b/rust/pspp/src/file/testdata/test.tlo
new file mode 100644 (file)
index 0000000..a53af34
Binary files /dev/null and b/rust/pspp/src/file/testdata/test.tlo differ
diff --git a/rust/pspp/src/file/testdata/test.xml b/rust/pspp/src/file/testdata/test.xml
new file mode 100644 (file)
index 0000000..f4c0fc1
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<visualization xmlns="http://www.ibm.com/software/analytics/spss/xml/visualization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creator="en" date="2021-01-03" description="Graph" id="visualization_1691" lang="en" name="GGRAPH" style="style1" type="27000100" version="3.0" xsi:schemaLocation="http://www.ibm.com/software/analytics/spss/xml/visualization http://www.ibm.com/software/analytics/spss/xml/visualization/vizml-3.0.xsd">
+  <!-- content redacted because this is enough for a file type test -->
+</visualization>
index 571c485c6c9c55d92c1f02b5aa72d2c0739a890d..e903b5b96f2024fd733135201f7fea8b21fbb51e 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -30,16 +30,15 @@ use unicode_width::UnicodeWidthStr;
 
 use crate::{
     data::{ByteString, Datum},
-    format::decimals::LANG_TO_DECIMAL,
+    locale::{Locale, LocaleFallbacks},
     sys::raw,
     util::ToSmallString,
     variable::{VarType, VarWidth},
 };
 
-mod decimals;
 mod display;
 mod parse;
-pub use display::{DisplayDatum, DisplayPlain, DisplayPlainF64};
+pub use display::{DisplayDatum, DisplayPlain, DisplayPlainF64, group_digits};
 
 #[derive(Clone, ThisError, Debug, PartialEq, Eq)]
 pub enum Error {
@@ -658,20 +657,20 @@ impl Format {
         // Verify that the format is right for the variable's type.
         self.type_.check_type_compatibility(var_width.into())?;
 
-        if let VarWidth::String(w) = var_width {
-            if var_width != self.var_width() {
-                let bad_spec = self;
-                let good_spec = if self.type_ == Type::A {
-                    Format { w, ..self }
-                } else {
-                    Format { w: w * 2, ..self }
-                };
-                return Err(Error::UnnamedStringVariableBadSpecWidth {
-                    width: w,
-                    bad_spec,
-                    good_spec,
-                });
-            }
+        if let VarWidth::String(w) = var_width
+            && var_width != self.var_width()
+        {
+            let bad_spec = self;
+            let good_spec = if self.type_ == Type::A {
+                Format { w, ..self }
+            } else {
+                Format { w: w * 2, ..self }
+            };
+            return Err(Error::UnnamedStringVariableBadSpecWidth {
+                width: w,
+                bad_spec,
+                good_spec,
+            });
         }
 
         Ok(self)
@@ -915,18 +914,23 @@ impl Decimal {
     }
 
     /// Returns the decimal point to use for the given `lang`, which should be a
-    /// language identifier like `en`, `de-DE`, `fr-FR`, etc.
-    pub fn for_lang(mut lang: &str) -> Self {
-        // Repeatedly strip a hyphenated suffix until we find something.
-        loop {
-            if let Some(decimal) = LANG_TO_DECIMAL.get(lang) {
-                return *decimal;
-            }
-            let Some((prefix, _suffix)) = lang.rsplit_once('-') else {
-                return Self::default();
-            };
-            lang = prefix;
-        }
+    /// language identifier like `en`, `de-DE`, `fr-FR`, etc.  If the decimal
+    /// point is not known for the given language, tries fallbacks (e.g. `fr` if
+    /// `fr-FR` is not available), and ultimately a global default if no
+    /// fallback can be found.
+    pub fn for_lang(lang: &str) -> Self {
+        LocaleFallbacks::new(lang)
+            .filter_map(Self::try_for_lang)
+            .next()
+            .unwrap_or_default()
+    }
+
+    /// Returns the decimal point to use for the given `lang`, which should be a
+    /// language identifier like `en`, `de-DE`, `fr-FR`, etc., or `None` if the
+    /// decimal point is not known for the given language or if the decimal
+    /// point isn't one of the ones we support.
+    pub fn try_for_lang(lang: &str) -> Option<Self> {
+        Locale::try_for_lang(lang).and_then(|locale| Self::try_from(locale.decimal()).ok())
     }
 }
 
@@ -974,22 +978,21 @@ pub struct Epoch(pub i32);
 impl Epoch {
     /// Applies the epoch to `year`:
     ///
-    /// - If `year` is 2 digits (between 0 and 99, inclusive), returns it
-    ///   converted it to the correct year considering the epoch.
+    /// - If `year` is 2 digits (in `0..=99`), returns it converted to the
+    ///   correct year considering the epoch.
     ///
     /// - Otherwise, returns `year` unchanged.
     pub fn apply(&self, year: i32) -> i32 {
-        match year {
-            0..=99 => {
-                let century = self.0 / 100 * 100;
-                let offset = self.0 - century;
-                if year >= offset {
-                    year + century
-                } else {
-                    year + century + 100
-                }
+        if (0..=99).contains(&year) {
+            let century = self.0 / 100 * 100;
+            let offset = self.0 - century;
+            if year >= offset {
+                year + century
+            } else {
+                year + century + 100
             }
-            other => other,
+        } else {
+            year
         }
     }
 }
@@ -1018,8 +1021,8 @@ impl CustomCurrencies {
     }
 
     pub fn set(&mut self, cc: CC, number_style: NumberStyle) {
-        if &number_style != &self[cc] {
-            Arc::make_mut(&mut self.map.get_or_insert_default())[cc] = Some(Box::new(number_style));
+        if number_style != self[cc] {
+            Arc::make_mut(self.map.get_or_insert_default())[cc] = Some(Box::new(number_style));
         }
     }
 }
@@ -1031,7 +1034,7 @@ impl Index<CC> for CustomCurrencies {
         if let Some(map) = &self.map
             && let Some(number_style) = &map[index]
         {
-            &**number_style
+            number_style
         } else {
             static DEFAULT: LazyLock<NumberStyle> =
                 LazyLock::new(|| NumberStyle::new(Decimal::Dot, false));
@@ -1150,7 +1153,7 @@ impl Settings {
         Self { epoch, ..self }
     }
     pub fn number_style(&self, type_: Type) -> &NumberStyle {
-        static NUMBER_STYLES: LazyLock<NumberStyles> = LazyLock::new(|| NumberStyles::new());
+        static NUMBER_STYLES: LazyLock<NumberStyles> = LazyLock::new(NumberStyles::new);
         NUMBER_STYLES.get(self, type_)
     }
 }
@@ -1502,7 +1505,12 @@ impl Iterator for DateTemplate {
 
 #[cfg(test)]
 mod tests {
-    use crate::format::{Decimal, Format, Type, Width};
+    use itertools::Itertools;
+
+    use crate::{
+        format::{Decimal, Format, Type, Width},
+        locale::LocaleFallbacks,
+    };
 
     #[test]
     fn codepage_to_unicode() {
@@ -1530,11 +1538,22 @@ mod tests {
 
     #[test]
     fn decimal() {
-        assert_eq!(Decimal::for_lang("en"), Decimal::Dot);
+        assert_eq!(Decimal::try_for_lang("en"), Some(Decimal::Dot));
+        assert_eq!(Decimal::try_for_lang("en-US"), None);
         assert_eq!(Decimal::for_lang("en-US"), Decimal::Dot);
-        assert_eq!(Decimal::for_lang("en-ES"), Decimal::Comma);
+        assert_eq!(Decimal::try_for_lang("en-ES"), Some(Decimal::Comma));
+        assert_eq!(Decimal::try_for_lang("fr"), Some(Decimal::Comma));
+        assert_eq!(Decimal::try_for_lang("fr-FR"), None);
         assert_eq!(Decimal::for_lang("fr-FR"), Decimal::Comma);
-        assert_eq!(Decimal::for_lang("ar"), Decimal::Dot);
-        assert_eq!(Decimal::for_lang("ar-LY"), Decimal::Comma);
+        assert_eq!(Decimal::try_for_lang("ar"), Some(Decimal::Dot));
+        assert_eq!(Decimal::try_for_lang("ar-LY"), Some(Decimal::Comma));
+    }
+
+    #[test]
+    fn locale_fallbacks() {
+        assert_eq!(
+            LocaleFallbacks::new("ca-ES-valencia").collect_vec(),
+            vec!["ca-ES-valencia", "ca-ES", "ca"]
+        );
     }
 }
diff --git a/rust/pspp/src/format/decimals.rs b/rust/pspp/src/format/decimals.rs
deleted file mode 100644 (file)
index 12c7e80..0000000
+++ /dev/null
@@ -1,412 +0,0 @@
-//! Autogenerated, do not edit by hand!
-//!
-//! The code in this file is generated by `build.rs`.  It is unconventional
-//! to check generated files into a repository, but it is done in this case
-//! because the source data is large and the generated code rarely needs to
-//! change.
-//!
-//! To regenerate this code, download a CLDR JSON release from
-//! <https://cldr.unicode.org/index/downloads>, rename it as
-//! `cldr-json-full.zip` in the same directory as `build.rs`,
-//! and touch `build.rs` to force a rebuild.
-use std::{collections::HashMap, sync::LazyLock};
-use crate::format::Decimal;
-
-/// Map from language to decimal point.
-pub static LANG_TO_DECIMAL: LazyLock<HashMap<&'static str, Decimal>> = LazyLock::new(|| {
-    let mut map = HashMap::new();
-    map.insert("aa", Decimal::Dot);
-    map.insert("ab", Decimal::Dot);
-    map.insert("af", Decimal::Comma);
-    map.insert("agq", Decimal::Comma);
-    map.insert("ak", Decimal::Dot);
-    map.insert("am", Decimal::Dot);
-    map.insert("an", Decimal::Dot);
-    map.insert("ann", Decimal::Dot);
-    map.insert("apc", Decimal::Dot);
-    map.insert("ar", Decimal::Dot);
-    map.insert("ar-DZ", Decimal::Comma);
-    map.insert("ar-LB", Decimal::Comma);
-    map.insert("ar-LY", Decimal::Comma);
-    map.insert("ar-MA", Decimal::Comma);
-    map.insert("ar-MR", Decimal::Comma);
-    map.insert("ar-TN", Decimal::Comma);
-    map.insert("arn", Decimal::Dot);
-    map.insert("as", Decimal::Dot);
-    map.insert("asa", Decimal::Dot);
-    map.insert("ast", Decimal::Comma);
-    map.insert("az", Decimal::Comma);
-    map.insert("az-Arab", Decimal::Dot);
-    map.insert("az-Arab-IQ", Decimal::Dot);
-    map.insert("az-Arab-TR", Decimal::Dot);
-    map.insert("ba", Decimal::Dot);
-    map.insert("bal", Decimal::Dot);
-    map.insert("bas", Decimal::Comma);
-    map.insert("be", Decimal::Comma);
-    map.insert("bem", Decimal::Dot);
-    map.insert("bew", Decimal::Dot);
-    map.insert("bez", Decimal::Dot);
-    map.insert("bg", Decimal::Comma);
-    map.insert("bgc", Decimal::Dot);
-    map.insert("bho", Decimal::Dot);
-    map.insert("blo", Decimal::Comma);
-    map.insert("blt", Decimal::Dot);
-    map.insert("bm", Decimal::Dot);
-    map.insert("bn", Decimal::Dot);
-    map.insert("bo", Decimal::Dot);
-    map.insert("bqi", Decimal::Dot);
-    map.insert("br", Decimal::Comma);
-    map.insert("brx", Decimal::Dot);
-    map.insert("bs", Decimal::Comma);
-    map.insert("bss", Decimal::Dot);
-    map.insert("bua", Decimal::Comma);
-    map.insert("byn", Decimal::Dot);
-    map.insert("ca", Decimal::Comma);
-    map.insert("cad", Decimal::Dot);
-    map.insert("cch", Decimal::Dot);
-    map.insert("ccp", Decimal::Dot);
-    map.insert("ce", Decimal::Dot);
-    map.insert("ceb", Decimal::Dot);
-    map.insert("cgg", Decimal::Dot);
-    map.insert("cho", Decimal::Dot);
-    map.insert("chr", Decimal::Dot);
-    map.insert("cic", Decimal::Dot);
-    map.insert("ckb", Decimal::Dot);
-    map.insert("co", Decimal::Dot);
-    map.insert("cop", Decimal::Dot);
-    map.insert("cs", Decimal::Comma);
-    map.insert("csw", Decimal::Dot);
-    map.insert("cu", Decimal::Dot);
-    map.insert("cv", Decimal::Comma);
-    map.insert("cy", Decimal::Dot);
-    map.insert("da", Decimal::Comma);
-    map.insert("dav", Decimal::Dot);
-    map.insert("de", Decimal::Comma);
-    map.insert("de-CH", Decimal::Dot);
-    map.insert("de-LI", Decimal::Dot);
-    map.insert("dje", Decimal::Dot);
-    map.insert("doi", Decimal::Dot);
-    map.insert("dsb", Decimal::Comma);
-    map.insert("dua", Decimal::Comma);
-    map.insert("dv", Decimal::Dot);
-    map.insert("dyo", Decimal::Comma);
-    map.insert("dz", Decimal::Dot);
-    map.insert("ebu", Decimal::Dot);
-    map.insert("ee", Decimal::Dot);
-    map.insert("el", Decimal::Comma);
-    map.insert("en", Decimal::Dot);
-    map.insert("en-AT", Decimal::Comma);
-    map.insert("en-BE", Decimal::Comma);
-    map.insert("en-CZ", Decimal::Comma);
-    map.insert("en-DE", Decimal::Comma);
-    map.insert("en-DK", Decimal::Comma);
-    map.insert("en-EE", Decimal::Comma);
-    map.insert("en-ES", Decimal::Comma);
-    map.insert("en-FI", Decimal::Comma);
-    map.insert("en-FR", Decimal::Comma);
-    map.insert("en-GE", Decimal::Comma);
-    map.insert("en-HU", Decimal::Comma);
-    map.insert("en-ID", Decimal::Comma);
-    map.insert("en-IT", Decimal::Comma);
-    map.insert("en-LT", Decimal::Comma);
-    map.insert("en-LV", Decimal::Comma);
-    map.insert("en-NL", Decimal::Comma);
-    map.insert("en-NO", Decimal::Comma);
-    map.insert("en-PL", Decimal::Comma);
-    map.insert("en-PT", Decimal::Comma);
-    map.insert("en-RO", Decimal::Comma);
-    map.insert("en-SE", Decimal::Comma);
-    map.insert("en-SI", Decimal::Comma);
-    map.insert("en-SK", Decimal::Comma);
-    map.insert("en-UA", Decimal::Comma);
-    map.insert("en-ZA", Decimal::Comma);
-    map.insert("eo", Decimal::Comma);
-    map.insert("es", Decimal::Comma);
-    map.insert("es-419", Decimal::Dot);
-    map.insert("es-BR", Decimal::Dot);
-    map.insert("es-BZ", Decimal::Dot);
-    map.insert("es-CU", Decimal::Dot);
-    map.insert("es-DO", Decimal::Dot);
-    map.insert("es-GT", Decimal::Dot);
-    map.insert("es-HN", Decimal::Dot);
-    map.insert("es-MX", Decimal::Dot);
-    map.insert("es-NI", Decimal::Dot);
-    map.insert("es-PA", Decimal::Dot);
-    map.insert("es-PE", Decimal::Dot);
-    map.insert("es-PR", Decimal::Dot);
-    map.insert("es-SV", Decimal::Dot);
-    map.insert("es-US", Decimal::Dot);
-    map.insert("et", Decimal::Comma);
-    map.insert("eu", Decimal::Comma);
-    map.insert("ewo", Decimal::Comma);
-    map.insert("fa", Decimal::Dot);
-    map.insert("ff", Decimal::Comma);
-    map.insert("ff-Adlm", Decimal::Dot);
-    map.insert("ff-Adlm-BF", Decimal::Dot);
-    map.insert("ff-Adlm-CM", Decimal::Dot);
-    map.insert("ff-Adlm-GH", Decimal::Dot);
-    map.insert("ff-Adlm-GM", Decimal::Dot);
-    map.insert("ff-Adlm-GW", Decimal::Dot);
-    map.insert("ff-Adlm-LR", Decimal::Dot);
-    map.insert("ff-Adlm-MR", Decimal::Dot);
-    map.insert("ff-Adlm-NE", Decimal::Dot);
-    map.insert("ff-Adlm-NG", Decimal::Dot);
-    map.insert("ff-Adlm-SL", Decimal::Dot);
-    map.insert("ff-Adlm-SN", Decimal::Dot);
-    map.insert("fi", Decimal::Comma);
-    map.insert("fil", Decimal::Dot);
-    map.insert("fo", Decimal::Comma);
-    map.insert("fr", Decimal::Comma);
-    map.insert("frr", Decimal::Dot);
-    map.insert("fur", Decimal::Comma);
-    map.insert("fy", Decimal::Comma);
-    map.insert("ga", Decimal::Dot);
-    map.insert("gaa", Decimal::Dot);
-    map.insert("gd", Decimal::Dot);
-    map.insert("gez", Decimal::Dot);
-    map.insert("gl", Decimal::Comma);
-    map.insert("gn", Decimal::Dot);
-    map.insert("gsw", Decimal::Dot);
-    map.insert("gu", Decimal::Dot);
-    map.insert("guz", Decimal::Dot);
-    map.insert("gv", Decimal::Dot);
-    map.insert("ha", Decimal::Dot);
-    map.insert("haw", Decimal::Dot);
-    map.insert("he", Decimal::Dot);
-    map.insert("hi", Decimal::Dot);
-    map.insert("hnj", Decimal::Dot);
-    map.insert("hr", Decimal::Comma);
-    map.insert("hsb", Decimal::Comma);
-    map.insert("ht", Decimal::Comma);
-    map.insert("hu", Decimal::Comma);
-    map.insert("hy", Decimal::Comma);
-    map.insert("ia", Decimal::Comma);
-    map.insert("id", Decimal::Comma);
-    map.insert("ie", Decimal::Comma);
-    map.insert("ig", Decimal::Dot);
-    map.insert("ii", Decimal::Dot);
-    map.insert("io", Decimal::Dot);
-    map.insert("is", Decimal::Comma);
-    map.insert("it", Decimal::Comma);
-    map.insert("it-CH", Decimal::Dot);
-    map.insert("iu", Decimal::Dot);
-    map.insert("ja", Decimal::Dot);
-    map.insert("jbo", Decimal::Dot);
-    map.insert("jgo", Decimal::Comma);
-    map.insert("jmc", Decimal::Dot);
-    map.insert("jv", Decimal::Comma);
-    map.insert("ka", Decimal::Comma);
-    map.insert("kaa", Decimal::Dot);
-    map.insert("kab", Decimal::Comma);
-    map.insert("kaj", Decimal::Dot);
-    map.insert("kam", Decimal::Dot);
-    map.insert("kcg", Decimal::Dot);
-    map.insert("kde", Decimal::Dot);
-    map.insert("kea", Decimal::Comma);
-    map.insert("kek", Decimal::Dot);
-    map.insert("ken", Decimal::Dot);
-    map.insert("kgp", Decimal::Comma);
-    map.insert("khq", Decimal::Dot);
-    map.insert("ki", Decimal::Dot);
-    map.insert("kk", Decimal::Comma);
-    map.insert("kk-Arab", Decimal::Dot);
-    map.insert("kkj", Decimal::Comma);
-    map.insert("kl", Decimal::Comma);
-    map.insert("kln", Decimal::Dot);
-    map.insert("km", Decimal::Dot);
-    map.insert("kn", Decimal::Dot);
-    map.insert("ko", Decimal::Dot);
-    map.insert("kok", Decimal::Dot);
-    map.insert("kpe", Decimal::Dot);
-    map.insert("ks", Decimal::Dot);
-    map.insert("ksb", Decimal::Dot);
-    map.insert("ksf", Decimal::Comma);
-    map.insert("ksh", Decimal::Comma);
-    map.insert("ku", Decimal::Comma);
-    map.insert("ku-Arab", Decimal::Dot);
-    map.insert("ku-Arab-IR", Decimal::Dot);
-    map.insert("kw", Decimal::Dot);
-    map.insert("kxv", Decimal::Dot);
-    map.insert("ky", Decimal::Comma);
-    map.insert("la", Decimal::Dot);
-    map.insert("lag", Decimal::Dot);
-    map.insert("lb", Decimal::Comma);
-    map.insert("lg", Decimal::Dot);
-    map.insert("lij", Decimal::Comma);
-    map.insert("lkt", Decimal::Dot);
-    map.insert("lld", Decimal::Comma);
-    map.insert("lmo", Decimal::Comma);
-    map.insert("ln", Decimal::Comma);
-    map.insert("lo", Decimal::Comma);
-    map.insert("lrc", Decimal::Dot);
-    map.insert("lt", Decimal::Comma);
-    map.insert("ltg", Decimal::Dot);
-    map.insert("lu", Decimal::Comma);
-    map.insert("luo", Decimal::Dot);
-    map.insert("luy", Decimal::Dot);
-    map.insert("lv", Decimal::Comma);
-    map.insert("lzz", Decimal::Dot);
-    map.insert("mai", Decimal::Dot);
-    map.insert("mas", Decimal::Dot);
-    map.insert("mdf", Decimal::Dot);
-    map.insert("mer", Decimal::Dot);
-    map.insert("mfe", Decimal::Dot);
-    map.insert("mg", Decimal::Dot);
-    map.insert("mgh", Decimal::Comma);
-    map.insert("mgo", Decimal::Dot);
-    map.insert("mhn", Decimal::Dot);
-    map.insert("mi", Decimal::Dot);
-    map.insert("mic", Decimal::Dot);
-    map.insert("mk", Decimal::Comma);
-    map.insert("ml", Decimal::Dot);
-    map.insert("mn", Decimal::Dot);
-    map.insert("mni", Decimal::Dot);
-    map.insert("moh", Decimal::Dot);
-    map.insert("mr", Decimal::Dot);
-    map.insert("ms", Decimal::Dot);
-    map.insert("ms-Arab-BN", Decimal::Comma);
-    map.insert("ms-BN", Decimal::Comma);
-    map.insert("ms-ID", Decimal::Comma);
-    map.insert("mt", Decimal::Dot);
-    map.insert("mua", Decimal::Comma);
-    map.insert("mus", Decimal::Dot);
-    map.insert("mww", Decimal::Dot);
-    map.insert("my", Decimal::Dot);
-    map.insert("myv", Decimal::Dot);
-    map.insert("mzn", Decimal::Dot);
-    map.insert("naq", Decimal::Dot);
-    map.insert("nb", Decimal::Comma);
-    map.insert("nd", Decimal::Dot);
-    map.insert("nds", Decimal::Comma);
-    map.insert("ne", Decimal::Dot);
-    map.insert("nl", Decimal::Comma);
-    map.insert("nmg", Decimal::Comma);
-    map.insert("nn", Decimal::Comma);
-    map.insert("nnh", Decimal::Comma);
-    map.insert("no", Decimal::Comma);
-    map.insert("nqo", Decimal::Dot);
-    map.insert("nr", Decimal::Comma);
-    map.insert("nso", Decimal::Dot);
-    map.insert("nus", Decimal::Dot);
-    map.insert("nv", Decimal::Dot);
-    map.insert("ny", Decimal::Dot);
-    map.insert("nyn", Decimal::Dot);
-    map.insert("oc", Decimal::Comma);
-    map.insert("oka", Decimal::Dot);
-    map.insert("om", Decimal::Dot);
-    map.insert("or", Decimal::Dot);
-    map.insert("os", Decimal::Comma);
-    map.insert("osa", Decimal::Dot);
-    map.insert("pa", Decimal::Dot);
-    map.insert("pap", Decimal::Dot);
-    map.insert("pcm", Decimal::Dot);
-    map.insert("pi", Decimal::Dot);
-    map.insert("pis", Decimal::Dot);
-    map.insert("pl", Decimal::Comma);
-    map.insert("pms", Decimal::Comma);
-    map.insert("prg", Decimal::Comma);
-    map.insert("ps", Decimal::Comma);
-    map.insert("pt", Decimal::Comma);
-    map.insert("qu", Decimal::Dot);
-    map.insert("qu-BO", Decimal::Comma);
-    map.insert("quc", Decimal::Dot);
-    map.insert("raj", Decimal::Dot);
-    map.insert("rhg", Decimal::Dot);
-    map.insert("rif", Decimal::Dot);
-    map.insert("rm", Decimal::Comma);
-    map.insert("rn", Decimal::Comma);
-    map.insert("ro", Decimal::Comma);
-    map.insert("rof", Decimal::Dot);
-    map.insert("ru", Decimal::Comma);
-    map.insert("rw", Decimal::Comma);
-    map.insert("rwk", Decimal::Dot);
-    map.insert("sa", Decimal::Dot);
-    map.insert("sah", Decimal::Comma);
-    map.insert("saq", Decimal::Dot);
-    map.insert("sat", Decimal::Dot);
-    map.insert("sbp", Decimal::Dot);
-    map.insert("sc", Decimal::Comma);
-    map.insert("scn", Decimal::Comma);
-    map.insert("sd", Decimal::Dot);
-    map.insert("sdh", Decimal::Dot);
-    map.insert("se", Decimal::Comma);
-    map.insert("seh", Decimal::Comma);
-    map.insert("ses", Decimal::Dot);
-    map.insert("sg", Decimal::Comma);
-    map.insert("sgs", Decimal::Dot);
-    map.insert("shi", Decimal::Comma);
-    map.insert("shn", Decimal::Dot);
-    map.insert("si", Decimal::Dot);
-    map.insert("sid", Decimal::Dot);
-    map.insert("sk", Decimal::Comma);
-    map.insert("skr", Decimal::Dot);
-    map.insert("sl", Decimal::Comma);
-    map.insert("sma", Decimal::Dot);
-    map.insert("smj", Decimal::Dot);
-    map.insert("smn", Decimal::Comma);
-    map.insert("sms", Decimal::Dot);
-    map.insert("sn", Decimal::Dot);
-    map.insert("so", Decimal::Dot);
-    map.insert("sq", Decimal::Comma);
-    map.insert("sr", Decimal::Comma);
-    map.insert("ss", Decimal::Comma);
-    map.insert("ssy", Decimal::Dot);
-    map.insert("st", Decimal::Dot);
-    map.insert("su", Decimal::Comma);
-    map.insert("suz", Decimal::Dot);
-    map.insert("sv", Decimal::Comma);
-    map.insert("sw", Decimal::Dot);
-    map.insert("sw-CD", Decimal::Comma);
-    map.insert("syr", Decimal::Dot);
-    map.insert("szl", Decimal::Comma);
-    map.insert("ta", Decimal::Dot);
-    map.insert("te", Decimal::Dot);
-    map.insert("teo", Decimal::Dot);
-    map.insert("tg", Decimal::Comma);
-    map.insert("th", Decimal::Dot);
-    map.insert("ti", Decimal::Dot);
-    map.insert("tig", Decimal::Dot);
-    map.insert("tk", Decimal::Comma);
-    map.insert("tn", Decimal::Dot);
-    map.insert("to", Decimal::Dot);
-    map.insert("tok", Decimal::Comma);
-    map.insert("tpi", Decimal::Dot);
-    map.insert("tr", Decimal::Comma);
-    map.insert("trv", Decimal::Dot);
-    map.insert("trw", Decimal::Dot);
-    map.insert("ts", Decimal::Comma);
-    map.insert("tt", Decimal::Comma);
-    map.insert("twq", Decimal::Dot);
-    map.insert("tyv", Decimal::Dot);
-    map.insert("tzm", Decimal::Comma);
-    map.insert("ug", Decimal::Dot);
-    map.insert("uk", Decimal::Comma);
-    map.insert("und", Decimal::Dot);
-    map.insert("ur", Decimal::Dot);
-    map.insert("uz", Decimal::Comma);
-    map.insert("vai", Decimal::Dot);
-    map.insert("ve", Decimal::Comma);
-    map.insert("vec", Decimal::Comma);
-    map.insert("vi", Decimal::Comma);
-    map.insert("vmw", Decimal::Comma);
-    map.insert("vo", Decimal::Dot);
-    map.insert("vun", Decimal::Dot);
-    map.insert("wa", Decimal::Dot);
-    map.insert("wae", Decimal::Comma);
-    map.insert("wal", Decimal::Dot);
-    map.insert("wbp", Decimal::Dot);
-    map.insert("wo", Decimal::Comma);
-    map.insert("xh", Decimal::Dot);
-    map.insert("xnr", Decimal::Dot);
-    map.insert("xog", Decimal::Dot);
-    map.insert("yav", Decimal::Comma);
-    map.insert("yi", Decimal::Dot);
-    map.insert("yo", Decimal::Dot);
-    map.insert("yrl", Decimal::Comma);
-    map.insert("yue", Decimal::Dot);
-    map.insert("za", Decimal::Dot);
-    map.insert("zgh", Decimal::Comma);
-    map.insert("zh", Decimal::Dot);
-    map.insert("zu", Decimal::Dot);
-    map
-});
index 1f2907cde2597d3db7b210032c43809ba3cd8308..2f1dc7f6237a70245dcd985eceaae79710d634f0 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -434,12 +434,7 @@ where
                 output.push_str(&style.prefix.s);
             }
             if let Some(grouping) = grouping {
-                for (i, digit) in magnitude[..integer_digits].bytes().enumerate() {
-                    if i > 0 && (integer_digits - i) % 3 == 0 {
-                        output.push(grouping.into());
-                    }
-                    output.push(digit as char);
-                }
+                output.extend(group_digits(&magnitude[..integer_digits], grouping.into()));
             } else {
                 output.push_str(&magnitude[..integer_digits]);
             }
@@ -576,11 +571,11 @@ where
             self.overflow(f)
         } else {
             let mut s = legacy.to_small_string::<40>();
-            if number < 0.0 {
-                if let Some(last) = s.pop() {
-                    let last = last.to_digit(10).unwrap();
-                    s.push(b"}JKLMNOPQR"[last as usize] as char);
-                }
+            if number < 0.0
+                && let Some(last) = s.pop()
+            {
+                let last = last.to_digit(10).unwrap();
+                s.push(b"}JKLMNOPQR"[last as usize] as char);
             }
             write!(f, "{}{s}", Zeros(w.saturating_sub(len)))
         }
@@ -785,7 +780,7 @@ where
                 let d1 = src.next().unwrap() - b'0';
                 output.push((d0 << 4) + d1);
             }
-            if digits % 2 != 0 {
+            if !digits.is_multiple_of(2) {
                 let d = src.next().unwrap() - b'0';
                 output.push(d << 4);
             }
@@ -1244,3 +1239,20 @@ pub fn endian_to_smallvec<const N: usize>(
     }
     vec
 }
+
+pub fn group_digits(s: &str, grouping: char) -> impl Iterator<Item = char> {
+    let n = s.len();
+    s.chars()
+        .enumerate()
+        .flat_map(move |(i, c)| {
+            [
+                if i > 0 && (n - i) % 3 == 0 {
+                    Some(grouping)
+                } else {
+                    None
+                },
+                Some(c),
+            ]
+        })
+        .flatten()
+}
index e0bbf84cc9b7f4efc547f6c502ca4011f91d6d46..2594c4e70f49fc1eb681ce781d0f41eadbf0dbf5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -24,7 +24,9 @@ use smallvec::SmallVec;
 
 use crate::{
     data::{ByteString, Datum, WithEncoding},
-    format::{AbstractFormat, CC, Epoch, Format, Settings, Type, UncheckedFormat},
+    format::{
+        AbstractFormat, CC, Epoch, Format, Settings, Type, UncheckedFormat, display::group_digits,
+    },
     lex::{Punct, Token, scan::StringScanner, segment::Syntax},
     settings::EndianSettings,
 };
@@ -1755,3 +1757,20 @@ fn dtime18_5() {
 fn dtime18_6() {
     test_times(Format::new(Type::DTime, 18, 6).unwrap(), "dtime18.6.txt");
 }
+
+#[test]
+fn grouping() {
+    fn grouped(s: &str) -> String {
+        group_digits(s, ',').collect()
+    }
+
+    assert_eq!(grouped("1"), "1");
+    assert_eq!(grouped("12"), "12");
+    assert_eq!(grouped("123"), "123");
+    assert_eq!(grouped("1234"), "1,234");
+    assert_eq!(grouped("12345"), "12,345");
+    assert_eq!(grouped("123456"), "123,456");
+    assert_eq!(grouped("1234567"), "1,234,567");
+    assert_eq!(grouped("12345678"), "12,345,678");
+    assert_eq!(grouped("123456789"), "123,456,789");
+}
index 45b37b57bc2fe94571f92c09f076cecf0cc6d4e9..0b51ac9a57208d1bc42f75bdf86fdad08f903857 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 25bb1d3e4581680235b4ff18ec63f6f6f53cc811..3ad1f6c7c6dd058ed16daf627b515c38eb3cb127 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 5422c534c1ec96377d8686e453064ee0681f7b18..c5f0e3ff4262ac427ef0d35af99b12ae69fd6cab 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 3f638a1e34cacdaaf35bdcedad61263d653aae1f..0b2a876e5dcee9dd160d2c98b9eadf024f0b87e7 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index f92407bb418b57694c06d1e846955533619c8e12..7c3b2e4ec4ffefa11af80a60b56154c01fbb6f7f 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index dfde500adaeb8a951656add1bc7cc426df5a1f43..46aef94f738d0b6c5d101793b26366e22fa807ab 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 319ff36d00f6a003426d84fe186f462ae278448a..9c42f2b54a4805ffd3385d9ceea44a38e52b8af5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -527,16 +527,15 @@ impl TokenSlice {
             start: Point { line: l0, .. },
             end: Point { line: l1, .. },
         }) = location.span
+            && let Some(file) = self.file()
         {
-            if let Some(file) = self.file() {
-                let lines = if l1 - l0 > 3 {
-                    vec![l0, l0 + 1, l1]
-                } else {
-                    (l0..=l1).collect()
-                };
-                for line_number in lines {
-                    source.push((line_number, file.get_line(line_number).to_string()));
-                }
+            let lines = if l1 - l0 > 3 {
+                vec![l0, l0 + 1, l1]
+            } else {
+                (l0..=l1).collect()
+            };
+            for line_number in lines {
+                source.push((line_number, file.get_line(line_number).to_string()));
             }
         }
 
index 4a3d2193f3abbaa34908092189d1a9bc2874ce6d..0dbf0751090e7825a6720d4be0d3d26b8728f534 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -150,7 +150,7 @@ impl Segment {
                         return Some(Err(ScanError::BadHexDigit(c)));
                     }
                 }
-                if s.len() % 2 != 0 {
+                if !s.len().is_multiple_of(2) {
                     return Some(Err(ScanError::OddLengthHexString(s.len())));
                 }
                 let bytes = s
index 6f0e582cda3f225ec0a83831e84f66de15ee485a..b40375f23b0936f6753b72ac5cf0caa6fa09ffe8 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 37840308459a0b3766a0d146c4d6277caace6338..1f108b73ad7d8a78077f49582169e0cf7f5e09f5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -436,12 +436,13 @@ fn match_char<F>(f: F, input: &str, eof: bool) -> Result<Option<&str>, Incomplet
 where
     F: Fn(char) -> bool,
 {
-    if let (Some(c), rest) = take(input, eof)? {
-        if f(c) {
-            return Ok(Some(rest));
-        }
+    if let (Some(c), rest) = take(input, eof)?
+        && f(c)
+    {
+        Ok(Some(rest))
+    } else {
+        Ok(None)
     }
-    Ok(None)
 }
 
 fn skip_spaces(mut input: &str, eof: bool) -> Result<&str, Incomplete> {
@@ -537,10 +538,10 @@ fn detect_command_name(input: &str, eof: bool) -> Result<bool, Incomplete> {
     }
     let command_name = command_name.trim_end_matches(|c: char| c.is_whitespace() || c == '.');
     for command in get_command_name_candidates(command_name) {
-        if let Some(m) = command_match(command, command_name) {
-            if m.missing_words <= 0 {
-                return Ok(true);
-            }
+        if let Some(m) = command_match(command, command_name)
+            && m.missing_words <= 0
+        {
+            return Ok(true);
         }
     }
     Ok(false)
@@ -552,12 +553,12 @@ impl Segmenter {
         input: &'a str,
         eof: bool,
     ) -> Result<Option<(&'a str, Segment)>, Incomplete> {
-        if let (Some('#'), rest) = take(input, eof)? {
-            if let (Some('!'), rest) = take(rest, eof)? {
-                let rest = self.parse_full_line(rest, eof)?;
-                self.state = (State::General, EnumSet::only(Substate::StartOfCommand));
-                return Ok(Some((rest, Segment::Shbang)));
-            }
+        if let (Some('#'), rest) = take(input, eof)?
+            && let (Some('!'), rest) = take(rest, eof)?
+        {
+            let rest = self.parse_full_line(rest, eof)?;
+            self.state = (State::General, EnumSet::only(Substate::StartOfCommand));
+            return Ok(Some((rest, Segment::Shbang)));
         }
 
         self.state = (
@@ -647,10 +648,10 @@ impl Segmenter {
                         return self.parse_number(rest, eof);
                     }
                     Some('.') => {
-                        if let (Some(c), _rest) = take(rest2, eof)? {
-                            if c.is_ascii_digit() {
-                                return self.parse_number(rest, eof);
-                            }
+                        if let (Some(c), _rest) = take(rest2, eof)?
+                            && c.is_ascii_digit()
+                        {
+                            return self.parse_number(rest, eof);
                         }
                     }
                     None | Some(_) => (),
index 12b6591c1c3d8b337ce76601293574af1a1251c1..dab1a9e165531eff916e94c97b0aebaef1059831 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index be0cad9f87b157dc9ff10a6235f5e10573f7c675..ad31d7a9d6ae9759e6979e58e0b16f8ee4899921 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 79fbb1295c0c271eee8598f0f30e622ec247f132..4e1d83cdd307925d8de787700edfbd8db6d96a9d 100644 (file)
@@ -20,7 +20,7 @@
 //!   and build and install it:
 //!
 //!   ```text
-//!   cargo install pspp
+//!   cargo install --locked pspp
 //!   ```
 //!
 //! - Clone a local copy of this repository and build and install it from there,
@@ -29,7 +29,7 @@
 //!   ```text
 //!   git clone git://git.sv.gnu.org/pspp
 //!   cd pspp/rust/pspp
-//!   cargo install --path .
+//!   cargo install --locked --path .
 //!   ```
 //!
 //! The above commands also work for upgrades.
@@ -85,7 +85,7 @@
 //!
 //! # License
 //!
-//! Copyright © 2025 Free Software Foundation, Inc.
+//! Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //!
 //! This program is free software: you can redistribute it and/or modify it under
 //! the terms of the [GNU General Public License] as published by the Free Software
@@ -113,11 +113,12 @@ pub mod hexfloat;
 pub mod identifier;
 pub mod integer;
 pub mod lex;
-pub mod locale_charset;
+pub mod locale;
 pub mod macros;
 pub mod message;
 pub mod output;
 pub mod pc;
+pub mod percentiles;
 pub mod por;
 pub mod prompt;
 pub mod settings;
@@ -139,7 +140,7 @@ pub(crate) fn element_offset<T>(slice: &[T], element: &T) -> Option<usize> {
 
     let byte_offset = elem_start.wrapping_sub(slice_start);
 
-    if byte_offset % size_of::<T>() != 0 {
+    if !byte_offset.is_multiple_of(size_of::<T>()) {
         return None;
     }
 
@@ -165,7 +166,7 @@ pub(crate) fn subslice_range<T>(slice: &[T], subslice: &[T]) -> Option<Range<usi
 
     let byte_start = subslice_start.wrapping_sub(slice_start);
 
-    if byte_start % size_of::<T>() != 0 {
+    if !byte_start.is_multiple_of(size_of::<T>()) {
         return None;
     }
 
diff --git a/rust/pspp/src/locale.rs b/rust/pspp/src/locale.rs
new file mode 100644 (file)
index 0000000..6ff59ed
--- /dev/null
@@ -0,0 +1,1499 @@
+//! PSPP localization data.
+//!
+//! For system-independence and compatibility with SPSS, PSPP uses locale data
+//! from the Unicode [Common Locale Data Repository (CLDR)].  This module
+//! contains localization data and data types for using it.
+//!
+//! This module would be unnecessary if Rust already had crates with good CLDR
+//! localization support.  As of this writing, it appears not to.
+//!
+//! PSPP incorporates a snapshot of all the locale data that it uses, which
+//! takes up some space (it is about 1.2 MiB of generated source code, which
+//! probably builds to about the same amount of binary).
+//!
+//! [Common Locale Data Repository (CLDR)]: https://github.com/unicode-org/cldr/
+#![warn(missing_docs, dead_code)]
+use chrono::{Datelike, Timelike};
+use enum_map::{Enum, EnumMap};
+use std::{
+    borrow::Cow,
+    char::REPLACEMENT_CHARACTER,
+    fmt::{Debug, Display, Write as _},
+    ops::Not,
+};
+
+use crate::{locale::data::LANG_TO_LOCALE, util::ToSmallString as _};
+
+mod charset;
+pub mod data;
+
+pub use charset::locale_charset;
+
+/// Progressively strips `-CC` and `_CC` suffixes from a locale.
+///
+/// For example, `LocaleFallbacks("ca-ES-valencia")` will yield, in order:
+///
+/// - `ca-ES-valencia`
+/// - `ca-ES`
+/// - `ca`
+pub struct LocaleFallbacks<'a>(Option<&'a str>);
+
+impl<'a> LocaleFallbacks<'a> {
+    /// Constructs a new `LocaleFallbacks` for stripping suffixes from `lang`.
+    pub fn new(lang: &'a str) -> Self {
+        Self(Some(lang))
+    }
+}
+
+impl<'a> Iterator for LocaleFallbacks<'a> {
+    type Item = &'a str;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let lang = self.0?;
+        self.0 = lang.rsplit_once(['-', '_']).map(|(stem, _suffix)| stem);
+        Some(lang)
+    }
+}
+
+/// Verbosity level for a date or time pattern.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum PatternType {
+    /// Tuesday, April 12, 1952 AD, 3:30:42pm PST
+    Full,
+    /// January 12, 1952, 3:30:32pm
+    Long,
+    /// Jan 12, 1952, 3:30pm
+    Medium,
+    /// 12.13.52, 3:30pm
+    Short,
+}
+
+/// Verbosity level for formatting names for days of the week.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum DayLength {
+    /// Sunday
+    Wide,
+    ///  Sun
+    Abbreviated,
+    /// Su
+    Short,
+    /// S
+    Narrow,
+}
+
+/// Verbosity level for formatting names for months.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum MonthLength {
+    /// January
+    Wide,
+    /// Jan
+    Abbreviated,
+    /// J
+    Narrow,
+}
+
+/// AM or PM.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum DayPeriod {
+    /// AM
+    Am,
+    /// PM
+    Pm,
+}
+
+/// Verbosity level for formatting [DayPeriod].
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum DayPeriodLength {
+    /// AM
+    Wide,
+    /// AM
+    Abbreviated,
+    /// a
+    Narrow,
+}
+
+/// CE or BCE.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum Era {
+    /// BCE (Before Common Era).
+    Bce,
+    /// CE (Common Era).
+    Ce,
+}
+
+/// A subset of [CLDR] data for one locale.
+///
+/// This only includes the locale data that PSPP currently uses.
+///
+/// [CLDR]: https://github.com/unicode-org/cldr/
+pub struct Locale<'a> {
+    lang: &'a str,
+    dates: EnumMap<PatternType, &'a str>,
+    times: EnumMap<PatternType, &'a str>,
+    date_times: EnumMap<PatternType, &'a str>,
+    months: EnumMap<MonthLength, [&'a str; 12]>,
+    days: EnumMap<DayLength, [&'a str; 7]>,
+    day_periods: EnumMap<DayPeriodLength, EnumMap<DayPeriod, &'a str>>,
+    eras: EnumMap<Era, &'a str>,
+    decimal: char,
+    group: char,
+}
+
+impl Debug for Locale<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Locale").field("lang", &self.lang).finish()
+    }
+}
+
+impl<'a> Locale<'a> {
+    /// Returns the locale identifier (e.g. `en`, `fr-FR`).
+    pub fn lang(&self) -> &'a str {
+        self.lang
+    }
+
+    /// Returns the local decimal point character.
+    ///
+    /// The decimal character might not be `.` or `,`, which are the only
+    /// decimal characters that PSPP supports in data formats.  Use
+    /// [Decimal::for_lang] to get a supported decimal character, or convert
+    /// from `char` to [Decimal] with `Decimal::try_from`.
+    ///
+    /// [Decimal::for_lang]: crate::format::Decimal::for_lang
+    /// [Decimal]: crate::format::Decimal
+    pub fn decimal(&self) -> char {
+        self.decimal
+    }
+
+    /// Returns the local grouping character.
+    ///
+    /// The grouping character might not be `.` or `,`, which are the only
+    /// grouping characters that PSPP supports in data formats.  Use
+    /// [Decimal::for_lang] to get a supported decimal character, or convert
+    /// from `char` to [Decimal] with `Decimal::try_from`, and then invert it
+    /// with `!` to get the data format grouping character.
+    ///
+    /// [Decimal::for_lang]: crate::format::Decimal::for_lang
+    /// [Decimal]: crate::format::Decimal
+    pub fn group(&self) -> char {
+        self.group
+    }
+
+    /// Returns the name of `era`.
+    pub fn era_name(&self, era: Era) -> &'a str {
+        self.eras[era]
+    }
+
+    /// Returns the name of `month` with verbosity `length`.
+    ///
+    /// # Panic
+    ///
+    /// Panics if `month` is not in the range `0..12`.
+    pub fn month_name(&self, length: MonthLength, month: u32) -> &'a str {
+        self.months[length][month as usize]
+    }
+
+    /// Returns the name of weekday `day` with verbosity `length`.
+    ///
+    /// # Panic
+    ///
+    /// Panics if `day` is not in the range `0..7`.
+    pub fn day_name(&self, length: DayLength, day: u32) -> &'a str {
+        self.days[length][day as usize]
+    }
+
+    /// Returns the name of day period `period` with verbosity `length`.
+    pub fn day_period_name(&self, length: DayPeriodLength, period: DayPeriod) -> &'a str {
+        self.day_periods[length][period]
+    }
+
+    /// Returns [DateFormat] for dates with the given `pattern` in this locale.
+    pub fn date_items(&self, pattern: PatternType) -> DateFormat<Pattern<'a>> {
+        Pattern::new(self.dates[pattern]).into_date_format()
+    }
+
+    /// Returns [DateFormat] for times with the given `pattern` in this locale.
+    pub fn time_items(&self, pattern: PatternType) -> DateFormat<Pattern<'a>> {
+        Pattern::new(self.times[pattern]).into_date_format()
+    }
+
+    /// Returns [DateFormat] for date-times with the given `date_pattern` and
+    /// `time_pattern` in this locale.
+    pub fn date_time_items(
+        &self,
+        time_pattern: PatternType,
+        date_pattern: PatternType,
+    ) -> DateFormat<Pattern<'a>> {
+        Pattern::new(self.date_times[time_pattern])
+            .with_time_and_date_formats(self.times[time_pattern], self.dates[date_pattern])
+            .into_date_format()
+    }
+
+    const fn new_patterns(
+        full: &'a str,
+        long: &'a str,
+        medium: &'a str,
+        short: &'a str,
+    ) -> EnumMap<PatternType, &'a str> {
+        EnumMap::from_array([full, long, medium, short])
+    }
+
+    const fn new_months(
+        wide: [&'a str; 12],
+        abbreviated: [&'a str; 12],
+        narrow: [&'a str; 12],
+    ) -> EnumMap<MonthLength, [&'a str; 12]> {
+        EnumMap::from_array([wide, abbreviated, narrow])
+    }
+
+    const fn new_days(
+        wide: [&'a str; 7],
+        abbreviated: [&'a str; 7],
+        short: [&'a str; 7],
+        narrow: [&'a str; 7],
+    ) -> EnumMap<DayLength, [&'a str; 7]> {
+        EnumMap::from_array([wide, abbreviated, short, narrow])
+    }
+
+    const fn new_day_periods(
+        wide: [&'a str; 2],
+        abbreviated: [&'a str; 2],
+        narrow: [&'a str; 2],
+    ) -> EnumMap<DayPeriodLength, EnumMap<DayPeriod, &'a str>> {
+        EnumMap::from_array([
+            EnumMap::from_array(wide),
+            EnumMap::from_array(abbreviated),
+            EnumMap::from_array(narrow),
+        ])
+    }
+
+    const fn new_eras(bce: &'a str, ce: &'a str) -> EnumMap<Era, &'a str> {
+        EnumMap::from_array([bce, ce])
+    }
+
+    /// Returns the `Locale` to use for the given `lang`, which should be a
+    /// language identifier like `en`, `de-DE`, `fr-FR`, etc.  If no locale is
+    /// known for the given language, tries fallbacks (e.g. `fr` if `fr-FR` is
+    /// not available), and ultimately a global default if no fallback can be
+    /// found.
+    pub fn for_lang(lang: &str) -> &'static Self {
+        LocaleFallbacks::new(lang)
+            .filter_map(Self::try_for_lang)
+            .next()
+            .unwrap_or(&data::EN)
+    }
+
+    /// Returns the `Locale` to use for the given `lang`, which should be a
+    /// language identifier like `en`, `de-DE`, `fr-FR`, etc., or `None` if the
+    /// decimal point is not known for the given language.
+    pub fn try_for_lang(lang: &str) -> Option<&'static Self> {
+        LANG_TO_LOCALE.get(lang).copied()
+    }
+}
+
+/// Verbosity level for the name of a time zone.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ZoneLength {
+    /// PDT
+    Short,
+    /// Pacific Daylight Time
+    Long,
+}
+
+/// Whether time is denominated as 12 or 24 hours.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum HourCount {
+    /// 12 hours (plus AM/PM).
+    Twelve,
+    /// 24 hours.
+    TwentyFour,
+}
+
+/// Whether hour numbering starts at 0 or at 1.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum HourBase {
+    /// 0 (0-11 or 0-23).
+    Zero,
+    /// 1 (1-12 or 1-24).
+    One,
+}
+
+impl Not for HourBase {
+    type Output = Self;
+
+    fn not(self) -> Self::Output {
+        match self {
+            HourBase::Zero => HourBase::One,
+            HourBase::One => HourBase::Zero,
+        }
+    }
+}
+
+/// Items that appear in time and date formats for Gregorian calendars.
+///
+/// These characters do not appear:
+///
+/// - `u`, `U`, `r` (years).
+///
+/// - `q` (quarters).
+///
+/// - `L` (months).
+///
+/// - `F`, `g` (days).
+///
+/// - `e` (days of week).
+///
+/// - `b` (day period).
+///
+/// - `A` (milliseconds in day).
+///
+/// - `Z`, `O`, `v`, `V`, `X`, `x` (zone).
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Item<'a> {
+    /// `G...`
+    ///
+    /// We only support the abbreviated form.
+    Era,
+    /// `yy`: 2-digit calendar year.
+    CalendarYear2,
+    /// `y...`: calendar year padded to at least the specified number of digits.
+    CalendarYear(
+        /// Number of `y`s.
+        usize,
+    ),
+    /// `YY`: 2-digit year in "week of year" systems.
+    ///
+    /// This is used with `w`.
+    WeekYear2,
+    /// `y...` in "week of year" systems, padded to at least the specified
+    /// number of digits.
+    WeekYear(
+        /// Number of `y`s.
+        usize,
+    ),
+    /// `Q`: Numerical quarter.
+    Quarter,
+    /// `M` or `MM`: Numerical month.
+    MonthNumber {
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `MMM...`: month name.
+    Month(
+        /// Month name verbosity.
+        MonthLength,
+    ),
+    /// `w...`: Week of year.
+    Week {
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `d` or `dd`: Day of month.
+    DayOfMonth {
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `D`: Day of year.
+    DayOfYear(
+        /// Minimum number of digits.
+        usize,
+    ),
+    /// `E...` or `c...`: Day of week.
+    DayOfWeek(
+        /// Weekday name verbosity.
+        DayLength,
+    ),
+    /// `a...`: AM or PM.
+    DayPeriod(
+        /// Verbosity level.
+        DayPeriodLength,
+    ),
+    /// `B...`: "at night" etc.
+    FlexibleDayPeriod(
+        /// Verbosity level.
+        DayPeriodLength,
+    ),
+    /// `h`, `hh`, `H`, `HH`, `k`, `kk`, `K`, `KK`: Hour of the day.
+    Hour {
+        /// Number of hours.
+        count: HourCount,
+        /// 0- or 1-based hours.
+        base: HourBase,
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `m` or `mm`: Minute.
+    Minute {
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `s` or `ss`: Second.
+    Second {
+        /// If true, pad 1-digit numbers with `0`.
+        pad: bool,
+    },
+    /// `S...`: Fractional second.
+    FractionalSecond {
+        /// Minimum number of fractional digits to show.
+        ///
+        /// The pattern syntax only supports `min_digits == max_digits`.
+        min_digits: usize,
+
+        /// Maximum number of fractional digits to show.
+        max_digits: usize,
+    },
+    /// `z...`: Time zone.
+    Zone(
+        /// Verbosity level.
+        ZoneLength,
+    ),
+    /// Unknown item.
+    Error {
+        /// Character.
+        c: char,
+        /// Number of repetitions of character.
+        n: usize,
+    },
+    /// Literal text.
+    Literal(
+        /// The text.
+        Cow<'a, str>,
+    ),
+}
+
+impl<'a> Display for Item<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match PatternToken::from(self) {
+            PatternToken::Variable { c, n } => {
+                for _ in 0..n {
+                    f.write_char(c)?;
+                }
+                Ok(())
+            }
+            PatternToken::Literal(literal) => {
+                fn split_start_matches<F>(s: &str, f: F) -> (&str, &str)
+                where
+                    F: Fn(char) -> bool,
+                {
+                    let tail_len = s.trim_start_matches(f).len();
+                    s.split_at(s.len() - tail_len)
+                }
+                fn split_end_matches<F>(s: &str, f: F) -> (&str, &str)
+                where
+                    F: Fn(char) -> bool,
+                {
+                    let head_len = s.trim_end_matches(f).len();
+                    s.split_at(head_len)
+                }
+                fn safe_without_quotes(c: char) -> bool {
+                    !c.is_ascii_alphabetic() && c != '\''
+                }
+                let (start, middle) = split_start_matches(literal, safe_without_quotes);
+                let (middle, end) = split_end_matches(middle, safe_without_quotes);
+                let (mid_start_quotes, middle) = split_start_matches(middle, |c| c == '\'');
+                let (middle, mid_end_quotes) = split_end_matches(middle, |c| c == '\'');
+
+                f.write_str(start)?;
+                for _ in 0..mid_start_quotes.len() {
+                    f.write_str("''")?;
+                }
+                if middle.chars().all(safe_without_quotes) {
+                    f.write_str(middle)?;
+                } else {
+                    f.write_char('\'')?;
+                    for c in middle.chars() {
+                        if c == '\'' {
+                            f.write_char('\'')?;
+                        }
+                        f.write_char(c)?;
+                    }
+                    f.write_char('\'')?;
+                }
+                for _ in 0..mid_end_quotes.len() {
+                    f.write_str("''")?;
+                }
+                f.write_str(end)
+            }
+        }
+    }
+}
+
+impl<'a> Item<'a> {
+    /// Returns true if this is [Item::Error].
+    pub fn is_error(&self) -> bool {
+        matches!(self, Item::Error { .. })
+    }
+
+    /// Returns true if this is a year item (for calendar-based years or
+    /// week-based years).
+    pub fn is_year(&self) -> bool {
+        matches!(
+            self,
+            Item::CalendarYear2 | Item::CalendarYear(_) | Item::WeekYear2 | Item::WeekYear(_)
+        )
+    }
+
+    /// Returns true if this is a month item (either a name or a number).
+    pub fn is_month(&self) -> bool {
+        matches!(self, Item::Month(_) | Item::MonthNumber { .. })
+    }
+
+    /// Returns true if this is a day item, either for day of month or day of
+    /// year (but not day of week).
+    pub fn is_day(&self) -> bool {
+        matches!(self, Item::DayOfMonth { .. } | Item::DayOfYear(_))
+    }
+
+    /// Returns true if this is [Item::Literal].
+    pub fn is_literal(&self) -> bool {
+        matches!(self, Self::Literal(_))
+    }
+
+    /// If this is [Item::Literal], returns its literal string.
+    pub fn as_literal(&self) -> Option<&str> {
+        match self {
+            Self::Literal(literal) => Some(literal),
+            _ => None,
+        }
+    }
+
+    /// Returns an object that implements [Display] for formatting `date_time`
+    /// and `utc_offset_seconds` with this `Item` in `locale`.
+    pub fn display<'b, D>(
+        &'a self,
+        locale: &'b Locale<'b>,
+        date_time: &'a D,
+        utc_offset_seconds: i32,
+    ) -> impl Display
+    where
+        D: Datelike + Timelike,
+    {
+        DisplayItem {
+            item: self,
+            locale,
+            date_time,
+            utc_offset_seconds,
+        }
+    }
+
+    /// Converts an item with any lifetime into an item with static lifetime, by
+    /// turning borrowed `Item::Literal`s into owned ones.
+    pub fn to_owned(self) -> Item<'static> {
+        match self {
+            Item::Era => Item::Era,
+            Item::CalendarYear2 => Item::CalendarYear2,
+            Item::CalendarYear(n) => Item::CalendarYear(n),
+            Item::WeekYear2 => Item::WeekYear2,
+            Item::WeekYear(n) => Item::WeekYear(n),
+            Item::Quarter => Item::Quarter,
+            Item::MonthNumber { pad } => Item::MonthNumber { pad },
+            Item::Month(month_length) => Item::Month(month_length),
+            Item::Week { pad } => Item::Week { pad },
+            Item::DayOfMonth { pad } => Item::DayOfMonth { pad },
+            Item::DayOfYear(n) => Item::DayOfYear(n),
+            Item::DayOfWeek(day_length) => Item::DayOfWeek(day_length),
+            Item::DayPeriod(day_period_length) => Item::DayPeriod(day_period_length),
+            Item::FlexibleDayPeriod(day_period_length) => {
+                Item::FlexibleDayPeriod(day_period_length)
+            }
+            Item::Hour { count, base, pad } => Item::Hour { count, base, pad },
+            Item::Minute { pad } => Item::Minute { pad },
+            Item::Second { pad } => Item::Second { pad },
+            Item::FractionalSecond {
+                min_digits,
+                max_digits,
+            } => Item::FractionalSecond {
+                min_digits,
+                max_digits,
+            },
+            Item::Zone(zone_length) => Item::Zone(zone_length),
+            Item::Error { c, n } => Item::Error { c, n },
+            Item::Literal(literal) => Item::Literal(Cow::from(literal.into_owned())),
+        }
+    }
+
+    /// For item types that include a `pad` boolean, returns its value.  Treats
+    /// [Item::CalendarYear] and [Item::WeekYear] as having a padding indication
+    /// that is true if their `n` is greater than 1.
+    pub fn pad(&self) -> Option<bool> {
+        match self {
+            Item::Era
+            | Item::CalendarYear2
+            | Item::WeekYear2
+            | Item::Quarter
+            | Item::Month(_)
+            | Item::DayOfYear(_)
+            | Item::DayOfWeek(_)
+            | Item::DayPeriod(_)
+            | Item::FlexibleDayPeriod(_)
+            | Item::FractionalSecond { .. }
+            | Item::Zone(_)
+            | Item::Error { .. }
+            | Item::Literal(_) => None,
+
+            Item::CalendarYear(n) | Item::WeekYear(n) => Some(*n > 1),
+
+            Item::MonthNumber { pad }
+            | Item::Week { pad }
+            | Item::DayOfMonth { pad }
+            | Item::Minute { pad }
+            | Item::Second { pad }
+            | Item::Hour { pad, .. } => Some(*pad),
+        }
+    }
+}
+
+struct DisplayItem<'a, 'b, D> {
+    item: &'a Item<'a>,
+    locale: &'b Locale<'b>,
+    date_time: &'a D,
+    utc_offset_seconds: i32,
+}
+
+impl<'a, 'b, D> Display for DisplayItem<'a, 'b, D>
+where
+    D: Datelike + Timelike,
+{
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        fn pad_width(pad: &bool) -> usize {
+            if *pad { 2 } else { 1 }
+        }
+
+        fn write_pad<T>(f: &mut std::fmt::Formatter<'_>, value: T, pad: &bool) -> std::fmt::Result
+        where
+            T: Display,
+        {
+            let width = pad_width(pad);
+            write!(f, "{value:0width$}")
+        }
+
+        match self.item {
+            Item::Era => {
+                let era = if self.date_time.year_ce().0 {
+                    Era::Ce
+                } else {
+                    Era::Bce
+                };
+                f.write_str(self.locale.era_name(era))
+            }
+            Item::CalendarYear2 => write!(f, "{:02}", self.date_time.year_ce().1 % 100),
+            Item::CalendarYear(n) => write!(f, "{:0n$}", self.date_time.year_ce().1),
+            Item::WeekYear2 => write!(f, "{:02}", self.date_time.iso_week().year() % 100),
+            Item::WeekYear(n) => write!(f, "{:0n$}", self.date_time.iso_week().year()),
+            Item::Quarter => write!(f, "{}", self.date_time.quarter()),
+            Item::MonthNumber { pad } => write_pad(f, self.date_time.month(), pad),
+            Item::Month(month_length) => f.write_str(
+                self.locale
+                    .month_name(*month_length, self.date_time.month0()),
+            ),
+            Item::Week { pad } => write_pad(f, self.date_time.iso_week().week(), pad),
+            Item::DayOfMonth { pad } => write_pad(f, self.date_time.day(), pad),
+            Item::DayOfYear(n) => write!(f, "{:0n$}", self.date_time.ordinal()),
+            Item::DayOfWeek(day_length) => f.write_str(
+                self.locale
+                    .day_name(*day_length, self.date_time.weekday().num_days_from_sunday()),
+            ),
+            Item::DayPeriod(day_period_length) | Item::FlexibleDayPeriod(day_period_length) => f
+                .write_str(self.locale.day_period_name(
+                    *day_period_length,
+                    if self.date_time.hour() < 12 {
+                        DayPeriod::Am
+                    } else {
+                        DayPeriod::Pm
+                    },
+                )),
+            Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::One,
+                pad,
+            } => write_pad(f, self.date_time.hour12().1, pad),
+            Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::Zero,
+                pad,
+            } => write_pad(f, self.date_time.hour() % 12, pad),
+            Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::One,
+                pad,
+            } => {
+                let hour = self.date_time.hour();
+                let hour = if hour == 0 { 24 } else { hour };
+                write_pad(f, hour, pad)
+            }
+            Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::Zero,
+                pad,
+            } => write_pad(f, self.date_time.hour(), pad),
+            Item::Minute { pad } => write_pad(f, self.date_time.minute(), pad),
+            Item::Second { pad } => write_pad(f, self.date_time.second(), pad),
+            Item::FractionalSecond {
+                min_digits,
+                max_digits,
+            } => {
+                let nanoseconds = self.date_time.nanosecond().min(999_999_999);
+                let mut s = format_args!("{nanoseconds:09}").to_small_string::<9>();
+                while s.len() < *min_digits {
+                    s.push('0');
+                }
+                while s.len() > *max_digits {
+                    s.pop();
+                }
+                f.write_str(&s)
+            }
+            Item::Zone(_) => {
+                let h = self.utc_offset_seconds / 3600;
+                let m = (self.utc_offset_seconds.abs() % 3600) / 60;
+                let s = self.utc_offset_seconds.abs() % 60;
+                match (h, m, s) {
+                    (0, 0, 0) => f.write_str("UTC"),
+                    (_, 0, 0) => write!(f, "UTC{:+}", self.utc_offset_seconds / 3600),
+                    (_, _, 0) => write!(f, "UTC{h:+03}:{m:02}"),
+                    (_, _, _) => write!(f, "UTC{h:+03}:{m:02}:{s:02}"),
+                }
+            }
+            Item::Error { .. } => f.write_char(REPLACEMENT_CHARACTER),
+            Item::Literal(literal) => f.write_str(literal),
+        }
+    }
+}
+
+/// A collection of [Item]s for formatting a date or time.
+#[derive(Clone, Debug)]
+pub struct DateFormat<T>(pub T);
+
+impl<T> DateFormat<T> {
+    /// Constructs a new `DateFormat` from `iter`.
+    pub fn new(iter: T) -> Self {
+        Self(iter)
+    }
+}
+
+impl<'a, T> DateFormat<T>
+where
+    T: Iterator<Item = Item<'a>> + Clone,
+{
+    /// Returns the first literal character in these items, which is likely to
+    /// be the main character that separates fields.
+    pub fn separator(&self) -> Option<char> {
+        for item in self.0.clone() {
+            if let Some(literal) = item.as_literal()
+                && let Some(c) = literal.chars().next()
+            {
+                return Some(c);
+            }
+        }
+        None
+    }
+
+    /// Returns an object that implements [Display] for formatting these date
+    /// and time items for `date_time` and `utc_offset_seconds` in `locale`.
+    pub fn display<'b, D>(
+        &self,
+        locale: &'b Locale<'b>,
+        date_time: &'a D,
+        utc_offset_seconds: i32,
+    ) -> impl Display
+    where
+        T: Iterator<Item = Item<'a>>,
+        D: Datelike + Timelike,
+    {
+        DisplayItems {
+            items: self.clone().into_iter(),
+            locale,
+            date_time,
+            utc_offset_seconds,
+        }
+    }
+
+    /// Returns an object that implements [Display] for a pattern that, parsed
+    /// back, would yield this [DateFormat].
+    pub fn display_pattern(&self) -> impl Display
+    where
+        T: Iterator<Item = Item<'a>>,
+    {
+        DisplayItemsPattern(self.clone().into_iter())
+    }
+}
+
+impl<'a, T> IntoIterator for DateFormat<T>
+where
+    T: IntoIterator<Item = Item<'a>> + Clone,
+{
+    type Item = Item<'a>;
+
+    type IntoIter = T::IntoIter;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.0.into_iter()
+    }
+}
+
+struct DisplayItems<'a, 'b, T, D> {
+    items: T,
+    locale: &'b Locale<'b>,
+    date_time: &'a D,
+    utc_offset_seconds: i32,
+}
+
+impl<'a, 'b, T, D> Display for DisplayItems<'a, 'b, T, D>
+where
+    T: IntoIterator<Item = Item<'a>> + Clone,
+    D: Datelike + Timelike,
+{
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        for item in self.items.clone() {
+            write!(
+                f,
+                "{}",
+                item.display(self.locale, self.date_time, self.utc_offset_seconds)
+            )?;
+        }
+        Ok(())
+    }
+}
+
+struct DisplayItemsPattern<T>(T);
+
+impl<'a, T> Display for DisplayItemsPattern<T>
+where
+    T: Iterator<Item = Item<'a>> + Clone,
+{
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let mut iter = self.0.clone().peekable();
+        let mut last = None;
+        while let Some(item) = iter.next() {
+            match PatternToken::from(&item) {
+                PatternToken::Variable { c, n } => {
+                    if last == Some(c) {
+                        // If we directly juxtapose two of the same variable,
+                        // then it changes the meaning and can even make the
+                        // variable expansion invalid.  Insert U+200B ZERO WIDTH
+                        // SPACE as a separator.
+                        f.write_char('\u{200b}')?;
+                    }
+                    for _ in 0..n {
+                        f.write_char(c)?;
+                    }
+                    last = Some(c);
+                }
+                PatternToken::Literal(literal) => {
+                    let mut s = None;
+                    while let Some(peek) = iter.peek()
+                        && let Some(next) = peek.as_literal()
+                    {
+                        s.get_or_insert_with(|| String::from(literal))
+                            .push_str(next);
+                        iter.next();
+                    }
+                    write!(
+                        f,
+                        "{}",
+                        Item::Literal(Cow::from(s.as_deref().unwrap_or(literal)))
+                    )?;
+                    last = None;
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Parses a date format pattern string into [Item]s.
+///
+/// [Date Format Patterns] defines the pattern syntax.
+///
+/// [Date Format Patterns]: https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+#[derive(Copy, Clone, Debug)]
+pub struct Pattern<'a> {
+    current: &'a str,
+    parent: Option<&'a str>,
+    subst: [&'a str; 2],
+}
+
+impl<'a> Pattern<'a> {
+    /// Constructs a new `Pattern` for parsing `format` from the syntax of CLDR
+    /// [Date Format Patterns].
+    ///
+    /// If `format` uses `{0}` and `{1}` variables, use
+    /// [Pattern::with_time_and_date_formats] to supply their substitutions.
+    ///
+    /// [Date Format Patterns]: https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
+    pub fn new(format: &'a str) -> Self {
+        Self {
+            current: format,
+            parent: None,
+            subst: ["", ""],
+        }
+    }
+
+    /// Returns this pattern with support for parsing `{0}` as `time_format` and
+    /// `{1}` as `date_format`.
+    pub fn with_time_and_date_formats(self, time_format: &'a str, date_format: &'a str) -> Self {
+        Self {
+            subst: [time_format, date_format],
+            ..self
+        }
+    }
+
+    /// Constructs a [DateFormat] for this pattern.
+    pub fn into_date_format(self) -> DateFormat<Pattern<'a>> {
+        DateFormat::new(self)
+    }
+}
+
+/// A token in a time and date pattern.
+enum PatternToken<'a> {
+    /// A variable substitution.
+    Variable {
+        /// The character.
+        c: char,
+        /// Character repeat count.
+        n: usize,
+    },
+    /// A literal.
+    Literal(
+        /// The literal string (with quotes stripped off).
+        &'a str,
+    ),
+}
+
+impl<'a> From<PatternToken<'a>> for Item<'a> {
+    fn from(value: PatternToken<'a>) -> Self {
+        match value {
+            PatternToken::Literal(literal) => Item::Literal(Cow::from(literal)),
+            PatternToken::Variable { c, n } => match (c, n) {
+                (_, 0) => unreachable!(),
+                ('G', 1..=5) => Item::Era,
+                ('y', 2) => Item::CalendarYear2,
+                ('y', n) => Item::CalendarYear(n),
+                ('Y', 2) => Item::WeekYear2,
+                ('Y', n) => Item::WeekYear(n),
+                ('Q', _) => Item::Quarter,
+                ('M', ..=1) => Item::MonthNumber { pad: false },
+                ('M', 2) => Item::MonthNumber { pad: true },
+                ('M', 3) => Item::Month(MonthLength::Abbreviated),
+                ('M', 4) => Item::Month(MonthLength::Wide),
+                ('M', 5) => Item::Month(MonthLength::Narrow),
+                ('w', 1 | 2) => Item::Week { pad: n > 1 },
+                ('d', 1 | 2) => Item::DayOfMonth { pad: n > 1 },
+                ('D', 1..=3) => Item::DayOfYear(n),
+                ('E' | 'c', 1..=3) => Item::DayOfWeek(DayLength::Abbreviated),
+                ('E' | 'c', 4) => Item::DayOfWeek(DayLength::Wide),
+                ('E' | 'c', 5) => Item::DayOfWeek(DayLength::Narrow),
+                ('E' | 'c', 6) => Item::DayOfWeek(DayLength::Short),
+                ('a', 1..=3) => Item::DayPeriod(DayPeriodLength::Abbreviated),
+                ('a', 4) => Item::DayPeriod(DayPeriodLength::Wide),
+                ('a', 5) => Item::DayPeriod(DayPeriodLength::Narrow),
+                ('B', 1..=3) => Item::FlexibleDayPeriod(DayPeriodLength::Abbreviated),
+                ('B', 4) => Item::FlexibleDayPeriod(DayPeriodLength::Wide),
+                ('B', 5) => Item::FlexibleDayPeriod(DayPeriodLength::Narrow),
+                ('h', 1 | 2) => Item::Hour {
+                    count: HourCount::Twelve,
+                    base: HourBase::One,
+                    pad: n > 1,
+                },
+                ('H', 1 | 2) => Item::Hour {
+                    count: HourCount::TwentyFour,
+                    base: HourBase::Zero,
+                    pad: n > 1,
+                },
+                ('K', 1 | 2) => Item::Hour {
+                    count: HourCount::Twelve,
+                    base: HourBase::Zero,
+                    pad: n > 1,
+                },
+                ('k', 1 | 2) => Item::Hour {
+                    count: HourCount::TwentyFour,
+                    base: HourBase::One,
+                    pad: n > 1,
+                },
+                ('m', 1 | 2) => Item::Minute { pad: n > 1 },
+                ('s', 1 | 2) => Item::Second { pad: n > 1 },
+                ('S', 1..) => Item::FractionalSecond {
+                    min_digits: n,
+                    max_digits: n,
+                },
+                ('z', 1..=3) => Item::Zone(ZoneLength::Short),
+                ('z', 4) => Item::Zone(ZoneLength::Long),
+                (c, n) => Item::Error { c, n },
+            },
+        }
+    }
+}
+
+impl<'a> From<&'a Item<'a>> for PatternToken<'a> {
+    fn from(value: &'a Item<'a>) -> Self {
+        fn padded(c: char, pad: &bool) -> (char, usize) {
+            (c, if *pad { 2 } else { 1 })
+        }
+
+        let (c, n) = match value {
+            Item::Era => ('G', 1),
+            Item::CalendarYear2 => ('y', 2),
+            Item::CalendarYear(n) => ('y', *n),
+            Item::WeekYear2 => ('Y', 2),
+            Item::WeekYear(n) => ('Y', *n),
+            Item::Quarter => ('Q', 1),
+            Item::MonthNumber { pad } => padded('M', pad),
+            Item::Month(MonthLength::Abbreviated) => ('M', 3),
+            Item::Month(MonthLength::Wide) => ('M', 4),
+            Item::Month(MonthLength::Narrow) => ('M', 5),
+            Item::Week { pad } => padded('w', pad),
+            Item::DayOfMonth { pad } => padded('d', pad),
+            Item::DayOfYear(n) => ('D', *n),
+            Item::DayOfWeek(DayLength::Abbreviated) => ('E', 3),
+            Item::DayOfWeek(DayLength::Wide) => ('E', 4),
+            Item::DayOfWeek(DayLength::Narrow) => ('E', 5),
+            Item::DayOfWeek(DayLength::Short) => ('E', 6),
+            Item::DayPeriod(DayPeriodLength::Abbreviated) => ('a', 3),
+            Item::DayPeriod(DayPeriodLength::Wide) => ('a', 4),
+            Item::DayPeriod(DayPeriodLength::Narrow) => ('a', 5),
+            Item::FlexibleDayPeriod(DayPeriodLength::Abbreviated) => ('B', 3),
+            Item::FlexibleDayPeriod(DayPeriodLength::Wide) => ('B', 4),
+            Item::FlexibleDayPeriod(DayPeriodLength::Narrow) => ('B', 5),
+            Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::One,
+                pad,
+            } => padded('h', pad),
+            Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::Zero,
+                pad,
+            } => padded('H', pad),
+            Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::Zero,
+                pad,
+            } => padded('K', pad),
+            Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::One,
+                pad,
+            } => padded('k', pad),
+            Item::Minute { pad } => padded('m', pad),
+            Item::Second { pad } => padded('s', pad),
+            Item::FractionalSecond {
+                min_digits: _,
+                max_digits,
+            } => ('S', *max_digits),
+            Item::Zone(ZoneLength::Short) => ('z', 3),
+            Item::Zone(ZoneLength::Long) => ('z', 4),
+            Item::Error { c, n } => (*c, *n),
+            Item::Literal(literal) => return PatternToken::Literal(literal),
+        };
+        PatternToken::Variable { c, n }
+    }
+}
+
+impl<'a> Iterator for Pattern<'a> {
+    type Item = Item<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        loop {
+            let mut iter = self.current.chars();
+            match iter.next() {
+                None => {
+                    self.current = self.parent.take()?;
+                }
+                Some(c) if c.is_ascii_alphabetic() => {
+                    let mut n = 1;
+                    loop {
+                        let rest = iter.as_str();
+                        if iter.next() != Some(c) {
+                            self.current = rest;
+                            return Some(PatternToken::Variable { c, n }.into());
+                        }
+                        n += 1;
+                    }
+                }
+                Some('\'') => {
+                    let rest = iter.as_str();
+                    if let Some(rest) = rest.strip_prefix('\'') {
+                        self.current = rest;
+                        return Some(Item::Literal(Cow::from("'")));
+                    } else if let Some(quote) = rest.find('\'') {
+                        let literal = if rest[quote + 1..].starts_with('\'') {
+                            &rest[..quote + 1]
+                        } else {
+                            &rest[..quote]
+                        };
+                        self.current = &rest[quote + 1..];
+                        return Some(Item::Literal(Cow::from(literal)));
+                    } else {
+                        self.current = "";
+                        return Some(Item::Literal(Cow::from(rest)));
+                    }
+                }
+                Some('{') => {
+                    let rest = iter.as_str();
+                    if self.parent.is_none()
+                        && let Some(c) = iter.next()
+                        && let Some(index) = c.to_digit(2)
+                        && let Some('}') = iter.next()
+                    {
+                        self.current = self.subst[index as usize];
+                        self.parent = Some(iter.as_str());
+                    } else {
+                        self.current = rest;
+                        return Some(Item::Literal(Cow::from("{")));
+                    }
+                }
+                Some(_) => {
+                    let rest = iter.as_str().trim_start_matches(|c: char| {
+                        !c.is_ascii_alphabetic() && c != '\'' && c != '{'
+                    });
+                    let literal = &self.current[..self.current.len() - rest.len()];
+                    self.current = rest;
+                    return Some(Item::Literal(Cow::from(literal)));
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::borrow::Cow;
+
+    use chrono::{NaiveDate, NaiveTime};
+    use itertools::Itertools as _;
+
+    use crate::locale::{
+        DayLength, DayPeriodLength, HourBase, HourCount, Item, MonthLength, Pattern, ZoneLength,
+        data::EN,
+    };
+
+    #[test]
+    fn date_format_parser() {
+        fn p(s: &str) -> Vec<Item<'_>> {
+            Pattern::new(s).collect_vec()
+        }
+
+        let expected = vec![
+            Item::CalendarYear(4),
+            Item::Literal(Cow::from(".")),
+            Item::MonthNumber { pad: true },
+            Item::Literal(Cow::from(".")),
+            Item::DayOfMonth { pad: true },
+            Item::Literal(Cow::from(" ")),
+            Item::Era,
+            Item::Literal(Cow::from(" ")),
+            Item::Literal(Cow::from("at")),
+            Item::Literal(Cow::from(" ")),
+            Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::Zero,
+                pad: true,
+            },
+            Item::Literal(Cow::from(":")),
+            Item::Minute { pad: true },
+            Item::Literal(Cow::from(":")),
+            Item::Second { pad: true },
+            Item::Literal(Cow::from(" ")),
+            Item::Zone(ZoneLength::Short),
+        ];
+        assert_eq!(&*p("yyyy.MM.dd G 'at' HH:mm:ss zzz"), &*expected);
+        assert_eq!(
+            &*Pattern::new("{1} 'at' {0}")
+                .with_time_and_date_formats("HH:mm:ss zzz", "yyyy.MM.dd G")
+                .collect_vec(),
+            &*expected
+        );
+
+        assert_eq!(p("G"), vec![Item::Era]);
+        assert_eq!(p("GG"), vec![Item::Era]);
+        assert_eq!(p("y"), vec![Item::CalendarYear(1)]);
+        assert_eq!(p("yy"), vec![Item::CalendarYear2]);
+        assert_eq!(p("yyy"), vec![Item::CalendarYear(3)]);
+        assert_eq!(p("yyyy"), vec![Item::CalendarYear(4)]);
+        assert_eq!(p("yyyyy"), vec![Item::CalendarYear(5)]);
+        assert_eq!(p("Y"), vec![Item::WeekYear(1)]);
+        assert_eq!(p("YY"), vec![Item::WeekYear2]);
+        assert_eq!(p("YYY"), vec![Item::WeekYear(3)]);
+        assert_eq!(p("YYYY"), vec![Item::WeekYear(4)]);
+        assert_eq!(p("YYYYY"), vec![Item::WeekYear(5)]);
+        assert_eq!(p("Q"), vec![Item::Quarter]);
+        assert_eq!(p("QQ"), vec![Item::Quarter]);
+        assert_eq!(p("M"), vec![Item::MonthNumber { pad: false }]);
+        assert_eq!(p("MM"), vec![Item::MonthNumber { pad: true }]);
+        assert_eq!(p("MMM"), vec![Item::Month(MonthLength::Abbreviated)]);
+        assert_eq!(p("MMMM"), vec![Item::Month(MonthLength::Wide)]);
+        assert_eq!(p("MMMMM"), vec![Item::Month(MonthLength::Narrow)]);
+        assert_eq!(p("w"), vec![Item::Week { pad: false }]);
+        assert_eq!(p("ww"), vec![Item::Week { pad: true }]);
+        assert_eq!(p("d"), vec![Item::DayOfMonth { pad: false }]);
+        assert_eq!(p("dd"), vec![Item::DayOfMonth { pad: true }]);
+        assert_eq!(p("E"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("EE"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("EEE"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("EEEE"), vec![Item::DayOfWeek(DayLength::Wide)]);
+        assert_eq!(p("EEEEE"), vec![Item::DayOfWeek(DayLength::Narrow)]);
+        assert_eq!(p("EEEEEE"), vec![Item::DayOfWeek(DayLength::Short)]);
+        assert_eq!(p("c"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("cc"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("ccc"), vec![Item::DayOfWeek(DayLength::Abbreviated)]);
+        assert_eq!(p("cccc"), vec![Item::DayOfWeek(DayLength::Wide)]);
+        assert_eq!(p("ccccc"), vec![Item::DayOfWeek(DayLength::Narrow)]);
+        assert_eq!(p("cccccc"), vec![Item::DayOfWeek(DayLength::Short)]);
+        assert_eq!(p("a"), vec![Item::DayPeriod(DayPeriodLength::Abbreviated)]);
+        assert_eq!(p("aa"), vec![Item::DayPeriod(DayPeriodLength::Abbreviated)]);
+        assert_eq!(
+            p("aaa"),
+            vec![Item::DayPeriod(DayPeriodLength::Abbreviated)]
+        );
+        assert_eq!(p("aaaa"), vec![Item::DayPeriod(DayPeriodLength::Wide)]);
+        assert_eq!(p("aaaaa"), vec![Item::DayPeriod(DayPeriodLength::Narrow)]);
+        assert_eq!(
+            p("B"),
+            vec![Item::FlexibleDayPeriod(DayPeriodLength::Abbreviated)]
+        );
+        assert_eq!(
+            p("BB"),
+            vec![Item::FlexibleDayPeriod(DayPeriodLength::Abbreviated)]
+        );
+        assert_eq!(
+            p("BBB"),
+            vec![Item::FlexibleDayPeriod(DayPeriodLength::Abbreviated)]
+        );
+        assert_eq!(
+            p("BBBB"),
+            vec![Item::FlexibleDayPeriod(DayPeriodLength::Wide)]
+        );
+        assert_eq!(
+            p("BBBBB"),
+            vec![Item::FlexibleDayPeriod(DayPeriodLength::Narrow)]
+        );
+        assert_eq!(
+            p("h"),
+            vec![Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::One,
+                pad: false
+            }]
+        );
+        assert_eq!(
+            p("hh"),
+            vec![Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::One,
+                pad: true
+            }]
+        );
+        assert_eq!(
+            p("H"),
+            vec![Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::Zero,
+                pad: false
+            }]
+        );
+        assert_eq!(
+            p("HH"),
+            vec![Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::Zero,
+                pad: true
+            }]
+        );
+        assert_eq!(
+            p("K"),
+            vec![Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::Zero,
+                pad: false
+            }]
+        );
+        assert_eq!(
+            p("KK"),
+            vec![Item::Hour {
+                count: HourCount::Twelve,
+                base: HourBase::Zero,
+                pad: true
+            }]
+        );
+        assert_eq!(
+            p("k"),
+            vec![Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::One,
+                pad: false
+            }]
+        );
+        assert_eq!(
+            p("kk"),
+            vec![Item::Hour {
+                count: HourCount::TwentyFour,
+                base: HourBase::One,
+                pad: true
+            }]
+        );
+        assert_eq!(p("m"), vec![Item::Minute { pad: false }]);
+        assert_eq!(p("mm"), vec![Item::Minute { pad: true }]);
+        assert_eq!(p("s"), vec![Item::Second { pad: false }]);
+        assert_eq!(p("ss"), vec![Item::Second { pad: true }]);
+        assert_eq!(
+            p("S"),
+            vec![Item::FractionalSecond {
+                min_digits: 1,
+                max_digits: 1
+            }]
+        );
+        assert_eq!(
+            p("SS"),
+            vec![Item::FractionalSecond {
+                min_digits: 2,
+                max_digits: 2
+            }]
+        );
+        assert_eq!(
+            p("SSS"),
+            vec![Item::FractionalSecond {
+                min_digits: 3,
+                max_digits: 3
+            }]
+        );
+        assert_eq!(
+            p("SSSS"),
+            vec![Item::FractionalSecond {
+                min_digits: 4,
+                max_digits: 4
+            }]
+        );
+        assert_eq!(p("z"), vec![Item::Zone(ZoneLength::Short)]);
+        assert_eq!(p("zz"), vec![Item::Zone(ZoneLength::Short)]);
+        assert_eq!(p("zzz"), vec![Item::Zone(ZoneLength::Short)]);
+        assert_eq!(p("zzzz"), vec![Item::Zone(ZoneLength::Long)]);
+        assert_eq!(p("RR"), vec![Item::Error { c: 'R', n: 2 }]);
+        assert_eq!(
+            p("''96"),
+            vec![
+                Item::Literal(Cow::from("'")),
+                Item::Literal(Cow::from("96"))
+            ]
+        );
+        assert_eq!(
+            p("'o''clock'"),
+            vec![
+                Item::Literal(Cow::from("o'")),
+                Item::Literal(Cow::from("clock"))
+            ]
+        );
+
+        for pattern in [
+            "G", "y", "yy", "yyy", "yyyy", "yyyyy", "Y", "YY", "YYY", "YYYY", "YYYYY", "Q", "M",
+            "MM", "MMM", "MMMM", "MMMMM", "w", "ww", "d", "dd", "D", "DD", "DDD", "EEE", "EEEE",
+            "EEEEE", "EEEEEE", "aaa", "aaaa", "aaaaa", "BBB", "BBBB", "BBBBB", "h", "hh", "H",
+            "HH", "k", "kk", "K", "KK", "m", "mm", "s", "ss", "S", "SS", "SSS", "SSSS", "SSSSS",
+            "SSSSSS", "zzz", "zzzz",
+        ] {
+            let items = p(pattern);
+            assert_eq!(items.len(), 1, "{:?}", items);
+            assert!(!items[0].is_error() && !items[0].is_literal());
+            assert_eq!(items[0].to_string(), pattern);
+        }
+    }
+
+    #[test]
+    fn date_formatting() {
+        let date = NaiveDate::from_ymd_opt(1996, 7, 10).unwrap();
+        let time = NaiveTime::from_hms_nano_opt(15, 8, 56, 089_000_000).unwrap();
+        let date_time = date.and_time(time);
+        for (pattern, expected) in [
+            ("G", "AD"),
+            ("y", "1996"),
+            ("yy", "96"),
+            ("yyy", "1996"),
+            ("yyyy", "1996"),
+            ("yyyyy", "01996"),
+            ("Y", "1996"),
+            ("YY", "96"),
+            ("YYY", "1996"),
+            ("YYYY", "1996"),
+            ("YYYYY", "01996"),
+            ("Q", "3"),
+            ("M", "7"),
+            ("MM", "07"),
+            ("MMM", "Jul"),
+            ("MMMM", "July"),
+            ("MMMMM", "J"),
+            ("w", "28"),
+            ("ww", "28"),
+            ("d", "10"),
+            ("dd", "10"),
+            ("D", "192"),
+            ("DD", "192"),
+            ("DDD", "192"),
+            ("E", "Wed"),
+            ("EE", "Wed"),
+            ("EEE", "Wed"),
+            ("EEEE", "Wednesday"),
+            ("EEEEE", "W"),
+            ("EEEEEE", "We"),
+            ("c", "Wed"),
+            ("cc", "Wed"),
+            ("ccc", "Wed"),
+            ("cccc", "Wednesday"),
+            ("ccccc", "W"),
+            ("cccccc", "We"),
+            ("a", "PM"),
+            ("aa", "PM"),
+            ("aaa", "PM"),
+            ("aaaa", "PM"),
+            ("aaaaa", "p"),
+            ("B", "PM"),
+            ("BB", "PM"),
+            ("BBB", "PM"),
+            ("BBBB", "PM"),
+            ("BBBBB", "p"),
+            ("h", "3"),
+            ("hh", "03"),
+            ("H", "15"),
+            ("HH", "15"),
+            ("K", "3"),
+            ("KK", "03"),
+            ("k", "15"),
+            ("kk", "15"),
+            ("m", "8"),
+            ("mm", "08"),
+            ("s", "56"),
+            ("ss", "56"),
+            ("S", "0"),
+            ("SS", "08"),
+            ("SSS", "089"),
+            ("SSSS", "0890"),
+            ("SSSSS", "08900"),
+            ("SSSSSS", "089000"),
+            ("SSSSSSS", "0890000"),
+            ("SSSSSSSS", "08900000"),
+            ("SSSSSSSSS", "089000000"),
+            ("SSSSSSSSSS", "0890000000"),
+            ("z", "UTC"),
+            ("zz", "UTC"),
+            ("zzz", "UTC"),
+            ("zzzz", "UTC"),
+            ("f", "\u{fffd}"),
+            // The examples from the CLDR spec's table of "Date Format Pattern
+            // Examples", lightly modified.
+            (
+                "yyyy.MM.dd G 'at' HH:mm:ss zzz",
+                "1996.07.10 AD at 15:08:56 UTC",
+            ),
+            ("EEE, MMM d, ''yy", "Wed, Jul 10, '96"),
+            ("h:mm a", "3:08 PM"),
+            ("hh 'o''clock' a, zzzz", "03 o'clock PM, UTC"),
+            ("K:mm a, z", "3:08 PM, UTC"),
+            ("yyyyy.MMMM.dd GGG hh:mm aaa", "01996.July.10 AD 03:08 PM"),
+        ] {
+            assert_eq!(
+                Pattern::new(pattern)
+                    .into_date_format()
+                    .display(&EN, &date_time, 0)
+                    .to_string(),
+                expected,
+                "pattern {pattern:?}"
+            );
+        }
+    }
+}
diff --git a/rust/pspp/src/locale/charset.rs b/rust/pspp/src/locale/charset.rs
new file mode 100644 (file)
index 0000000..bf85d73
--- /dev/null
@@ -0,0 +1,304 @@
+// Determine a canonical name for the current locale's character encoding.
+//
+// Copyright (C) 2000-2006, 2008-2023, 2025, 2026 Free Software Foundation, Inc.
+//
+// This file is free software: you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 2.1 of the License, or (at your option)
+// any later version.
+//
+// This file is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+// A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+//
+// Written by Bruno Haible <bruno@clisp.org>.  Translated to Rust by Ben Pfaff
+// <blp@cs.stanford.edu>.
+
+use std::sync::LazyLock;
+
+fn map_aliases(s: &str) -> &'static str {
+    #[cfg(target_os = "freebsd")]
+    match s {
+        "ARMSCII-8" => return "ARMSCII-8",
+        "Big5" => return "BIG5",
+        "C" => return "ASCII",
+        "CP1131" => return "CP1131",
+        "CP1251" => return "CP1251",
+        "CP866" => return "CP866",
+        "GB18030" => return "GB18030",
+        "GB2312" => return "GB2312",
+        "GBK" => return "GBK",
+        "ISCII-DEV" => return "?",
+        "ISO8859-1" => return "ISO-8859-1",
+        "ISO8859-13" => return "ISO-8859-13",
+        "ISO8859-15" => return "ISO-8859-15",
+        "ISO8859-2" => return "ISO-8859-2",
+        "ISO8859-5" => return "ISO-8859-5",
+        "ISO8859-7" => return "ISO-8859-7",
+        "ISO8859-9" => return "ISO-8859-9",
+        "KOI8-R" => return "KOI8-R",
+        "KOI8-U" => return "KOI8-U",
+        "SJIS" => return "SHIFT_JIS",
+        "US-ASCII" => return "ASCII",
+        "eucCN" => return "GB2312",
+        "eucJP" => return "EUC-JP",
+        "eucKR" => return "EUC-KR",
+        _ => (),
+    };
+
+    #[cfg(target_os = "netbsd")]
+    match s {
+        "646" => return "ASCII",
+        "ARMSCII-8" => return "ARMSCII-8",
+        "BIG5" => return "BIG5",
+        "Big5-HKSCS" => return "BIG5-HKSCS",
+        "CP1251" => return "CP1251",
+        "CP866" => return "CP866",
+        "GB18030" => return "GB18030",
+        "GB2312" => return "GB2312",
+        "ISO8859-1" => return "ISO-8859-1",
+        "ISO8859-13" => return "ISO-8859-13",
+        "ISO8859-15" => return "ISO-8859-15",
+        "ISO8859-2" => return "ISO-8859-2",
+        "ISO8859-4" => return "ISO-8859-4",
+        "ISO8859-5" => return "ISO-8859-5",
+        "ISO8859-7" => return "ISO-8859-7",
+        "KOI8-R" => return "KOI8-R",
+        "KOI8-U" => return "KOI8-U",
+        "PT154" => return "PT154",
+        "SJIS" => return "SHIFT_JIS",
+        "eucCN" => return "GB2312",
+        "eucJP" => return "EUC-JP",
+        "eucKR" => return "EUC-KR",
+        "eucTW" => return "EUC-TW",
+        _ => (),
+    };
+
+    #[cfg(target_os = "openbsd")]
+    match s {
+        "646" => return "ASCII",
+        "ISO8859-1" => return "ISO-8859-1",
+        "ISO8859-13" => return "ISO-8859-13",
+        "ISO8859-15" => return "ISO-8859-15",
+        "ISO8859-2" => return "ISO-8859-2",
+        "ISO8859-4" => return "ISO-8859-4",
+        "ISO8859-5" => return "ISO-8859-5",
+        "ISO8859-7" => return "ISO-8859-7",
+        "US-ASCII" => return "ASCII",
+        _ => (),
+    };
+
+    /* Darwin 7.5 has nl_langinfo(CODESET), but sometimes its value is
+      useless:
+      - It returns the empty string when LANG is set to a locale of the
+        form ll_CC, although ll_CC/LC_CTYPE is a symlink to an UTF-8
+        LC_CTYPE file.
+      - The environment variables LANG, LC_CTYPE, LC_ALL are not set by
+        the system; nl_langinfo(CODESET) returns "US-ASCII" in this case.
+      - The documentation says:
+          "... all code that calls BSD system routines should ensure
+           that the const *char parameters of these routines are in UTF-8
+           encoding. All BSD system functions expect their string
+           parameters to be in UTF-8 encoding and nothing else."
+        It also says
+          "An additional caveat is that string parameters for files,
+           paths, and other file-system entities must be in canonical
+           UTF-8. In a canonical UTF-8 Unicode string, all decomposable
+           characters are decomposed ..."
+        but this is not true: You can pass non-decomposed UTF-8 strings
+        to file system functions, and it is the OS which will convert
+        them to decomposed UTF-8 before accessing the file system.
+      - The Apple Terminal application displays UTF-8 by default.
+      - However, other applications are free to use different encodings:
+        - xterm uses ISO-8859-1 by default.
+        - TextEdit uses MacRoman by default.
+      We prefer UTF-8 over decomposed UTF-8-MAC because one should
+      minimize the use of decomposed Unicode. Unfortunately, through the
+      Darwin file system, decomposed UTF-8 strings are leaked into user
+      space nevertheless.
+      Then there are also the locales with encodings other than US-ASCII
+      and UTF-8. These locales can be occasionally useful to users (e.g.
+      when grepping through ISO-8859-1 encoded text files), when all their
+      file names are in US-ASCII.
+    */
+
+    #[cfg(target_os = "macos")]
+    match s {
+        "ARMSCII-8" => return "ARMSCII-8",
+        "Big5" => return "BIG5",
+        "Big5HKSCS" => return "BIG5-HKSCS",
+        "CP1131" => return "CP1131",
+        "CP1251" => return "CP1251",
+        "CP866" => return "CP866",
+        "CP949" => return "CP949",
+        "GB18030" => return "GB18030",
+        "GB2312" => return "GB2312",
+        "GBK" => return "GBK",
+        "ISO8859-1" => return "ISO-8859-1",
+        "ISO8859-13" => return "ISO-8859-13",
+        "ISO8859-15" => return "ISO-8859-15",
+        "ISO8859-2" => return "ISO-8859-2",
+        "ISO8859-4" => return "ISO-8859-4",
+        "ISO8859-5" => return "ISO-8859-5",
+        "ISO8859-7" => return "ISO-8859-7",
+        "ISO8859-9" => return "ISO-8859-9",
+        "KOI8-R" => return "KOI8-R",
+        "KOI8-U" => return "KOI8-U",
+        "PT154" => return "PT154",
+        "SJIS" => return "SHIFT_JIS",
+        "eucCN" => return "GB2312",
+        "eucJP" => return "EUC-JP",
+        "eucKR" => return "EUC-KR",
+        _ => (),
+    };
+
+    #[cfg(target_os = "aix")]
+    match s {
+        "GBK" => return "GBK",
+        "IBM-1046" => return "CP1046",
+        "IBM-1124" => return "CP1124",
+        "IBM-1129" => return "CP1129",
+        "IBM-1252" => return "CP1252",
+        "IBM-850" => return "CP850",
+        "IBM-856" => return "CP856",
+        "IBM-921" => return "ISO-8859-13",
+        "IBM-922" => return "CP922",
+        "IBM-932" => return "CP932",
+        "IBM-943" => return "CP943",
+        "IBM-eucCN" => return "GB2312",
+        "IBM-eucJP" => return "EUC-JP",
+        "IBM-eucKR" => return "EUC-KR",
+        "IBM-eucTW" => return "EUC-TW",
+        "ISO8859-1" => return "ISO-8859-1",
+        "ISO8859-15" => return "ISO-8859-15",
+        "ISO8859-2" => return "ISO-8859-2",
+        "ISO8859-5" => return "ISO-8859-5",
+        "ISO8859-6" => return "ISO-8859-6",
+        "ISO8859-7" => return "ISO-8859-7",
+        "ISO8859-8" => return "ISO-8859-8",
+        "ISO8859-9" => return "ISO-8859-9",
+        "TIS-620" => return "TIS-620",
+        "UTF-8" => return "UTF-8",
+        "big5" => return "BIG5",
+        _ => (),
+    };
+
+    #[cfg(windows)]
+    match s {
+        "CP1361" => return "JOHAB",
+        "CP20127" => return "ASCII",
+        "CP20866" => return "KOI8-R",
+        "CP20936" => return "GB2312",
+        "CP21866" => return "KOI8-RU",
+        "CP28591" => return "ISO-8859-1",
+        "CP28592" => return "ISO-8859-2",
+        "CP28593" => return "ISO-8859-3",
+        "CP28594" => return "ISO-8859-4",
+        "CP28595" => return "ISO-8859-5",
+        "CP28596" => return "ISO-8859-6",
+        "CP28597" => return "ISO-8859-7",
+        "CP28598" => return "ISO-8859-8",
+        "CP28599" => return "ISO-8859-9",
+        "CP28605" => return "ISO-8859-15",
+        "CP38598" => return "ISO-8859-8",
+        "CP51932" => return "EUC-JP",
+        "CP51936" => return "GB2312",
+        "CP51949" => return "EUC-KR",
+        "CP51950" => return "EUC-TW",
+        "CP54936" => return "GB18030",
+        "CP65001" => return "UTF-8",
+        "CP936" => return "GBK",
+        _ => (),
+    };
+
+    String::from(s).leak()
+}
+
+#[cfg(unix)]
+mod inner {
+    use std::{
+        ffi::{CStr, CString, c_int},
+        ptr::null,
+    };
+
+    use libc::{self, CODESET, LC_CTYPE, nl_langinfo, setlocale};
+
+    unsafe fn string_from_pointer(s: *const i8) -> Option<String> {
+        if s.is_null() {
+            None
+        } else {
+            Some(unsafe { CStr::from_ptr(s).to_string_lossy().into() })
+        }
+    }
+
+    fn set_locale(category: c_int, locale: Option<&str>) -> Option<String> {
+        unsafe {
+            let locale = locale.map(|s| CString::new(s).unwrap());
+            let locale_ptr = locale.as_ref().map_or(null(), |s| s.as_ptr());
+            string_from_pointer(setlocale(category, locale_ptr))
+        }
+    }
+
+    pub fn locale_charset() -> Option<String> {
+        unsafe {
+            let saved_locale = set_locale(LC_CTYPE, None);
+            set_locale(LC_CTYPE, Some(""));
+            let codeset = string_from_pointer(nl_langinfo(CODESET));
+            set_locale(LC_CTYPE, saved_locale.as_deref());
+            codeset
+        }
+    }
+}
+
+#[cfg(windows)]
+mod inner {
+    use libc::{LC_CTYPE, setlocale};
+    use std::ffi::{CStr, CString};
+    use windows_sys::Win32::Globalization::GetACP;
+
+    fn current_locale() -> Option<String> {
+        unsafe {
+            let empty_cstr = CString::new("").unwrap();
+            let locale = setlocale(LC_CTYPE, empty_cstr.as_ptr());
+            if locale.is_null() {
+                None
+            } else {
+                Some(CStr::from_ptr(locale).to_string_lossy().into())
+            }
+        }
+    }
+
+    pub fn locale_charset() -> Option<String> {
+        let Some(current_locale) = current_locale() else {
+            return None;
+        };
+        let codepage = if let Some((_, pdot)) = current_locale.rsplit_once('.') {
+            format!("CP{pdot}")
+        } else {
+            format!("CP{}", unsafe { GetACP() })
+        };
+        Some(match codepage.as_str() {
+            "CP65001" | "CPutf8" => String::from("UTF-8"),
+            _ => codepage,
+        })
+    }
+}
+
+#[cfg(not(any(unix, windows)))]
+mod inner {
+    pub fn locale_charse() -> String {
+        String::from("UTF-8")
+    }
+}
+
+/// Returns the character set used by the locale configured in the operating
+/// system.
+pub fn locale_charset() -> &'static str {
+    static LOCALE_CHARSET: LazyLock<&'static str> =
+        LazyLock::new(|| map_aliases(&inner::locale_charset().unwrap_or(String::from("UTF-8"))));
+    &LOCALE_CHARSET
+}
diff --git a/rust/pspp/src/locale/data.rs b/rust/pspp/src/locale/data.rs
new file mode 100644 (file)
index 0000000..37ebb06
--- /dev/null
@@ -0,0 +1,36594 @@
+//! Autogenerated locale data for all of the CLDR locales.
+//!
+//! The code in this file is generated by `build.rs`.  It is unconventional
+//! to check generated files into a repository, but it is done in this case
+//! because the source data is large and the generated code rarely needs to
+//! change.
+//!
+//! To regenerate this code, download a CLDR JSON release from
+//! <https://cldr.unicode.org/index/downloads>, rename it as
+//! `cldr-json-full.zip` in the same directory as `build.rs`,
+//! and touch `build.rs` to force a rebuild.
+//!
+//! The CLDR is covered by the Unicode License v3, available at
+//! <https://github.com/unicode-org/cldr/blob/main/LICENSE>.
+
+use crate::locale::Locale;
+use std::{collections::HashMap, sync::LazyLock};
+/// Data for `aa` locale.
+pub static AA: Locale<'static> = Locale {
+    lang: "aa",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `aa-DJ` locale.
+pub static AA_DJ: Locale<'static> = Locale {
+    lang: "aa-DJ",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `aa-ER` locale.
+pub static AA_ER: Locale<'static> = Locale {
+    lang: "aa-ER",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ab` locale.
+pub static AB: Locale<'static> = Locale {
+    lang: "ab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `af` locale.
+pub static AF: Locale<'static> = Locale {
+    lang: "af",
+    dates: Locale::new_patterns("EEEE dd MMMM y", "dd MMMM y", "dd MMM y", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januarie",
+            "Februarie",
+            "Maart",
+            "April",
+            "Mei",
+            "Junie",
+            "Julie",
+            "Augustus",
+            "September",
+            "Oktober",
+            "November",
+            "Desember",
+        ],
+        [
+            "Jan.", "Feb.", "Mrt.", "Apr.", "Mei", "Jun.", "Jul.", "Aug.", "Sep.", "Okt.", "Nov.",
+            "Des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sondag",
+            "Maandag",
+            "Dinsdag",
+            "Woensdag",
+            "Donderdag",
+            "Vrydag",
+            "Saterdag",
+        ],
+        ["So.", "Ma.", "Di.", "Wo.", "Do.", "Vr.", "Sa."],
+        ["So.", "Ma.", "Di.", "Wo.", "Do.", "Vr.", "Sa."],
+        ["S", "M", "D", "W", "D", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["vm.", "nm."], ["vm.", "nm."], ["v", "n"]),
+    eras: Locale::new_eras("v.C.", "n.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `af-NA` locale.
+pub static AF_NA: Locale<'static> = Locale {
+    lang: "af-NA",
+    dates: Locale::new_patterns("EEEE dd MMMM y", "dd MMMM y", "dd MMM y", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januarie",
+            "Februarie",
+            "Maart",
+            "April",
+            "Mei",
+            "Junie",
+            "Julie",
+            "Augustus",
+            "September",
+            "Oktober",
+            "November",
+            "Desember",
+        ],
+        [
+            "Jan.", "Feb.", "Mrt.", "Apr.", "Mei", "Jun.", "Jul.", "Aug.", "Sep.", "Okt.", "Nov.",
+            "Des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sondag",
+            "Maandag",
+            "Dinsdag",
+            "Woensdag",
+            "Donderdag",
+            "Vrydag",
+            "Saterdag",
+        ],
+        ["So.", "Ma.", "Di.", "Wo.", "Do.", "Vr.", "Sa."],
+        ["So.", "Ma.", "Di.", "Wo.", "Do.", "Vr.", "Sa."],
+        ["S", "M", "D", "W", "D", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["vm.", "nm."], ["vm.", "nm."], ["v", "n"]),
+    eras: Locale::new_eras("v.C.", "n.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `agq` locale.
+pub static AGQ: Locale<'static> = Locale {
+    lang: "agq",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ndzɔ̀ŋɔ̀nùm",
+            "ndzɔ̀ŋɔ̀kƗ̀zùʔ",
+            "ndzɔ̀ŋɔ̀tƗ̀dʉ̀ghà",
+            "ndzɔ̀ŋɔ̀tǎafʉ̄ghā",
+            "ndzɔ̀ŋèsèe",
+            "ndzɔ̀ŋɔ̀nzùghò",
+            "ndzɔ̀ŋɔ̀dùmlo",
+            "ndzɔ̀ŋɔ̀kwîfɔ̀e",
+            "ndzɔ̀ŋɔ̀tƗ̀fʉ̀ghàdzughù",
+            "ndzɔ̀ŋɔ̀ghǔuwelɔ̀m",
+            "ndzɔ̀ŋɔ̀chwaʔàkaa wo",
+            "ndzɔ̀ŋèfwòo",
+        ],
+        [
+            "nùm", "kɨz", "tɨd", "taa", "see", "nzu", "dum", "fɔe", "dzu", "lɔm", "kaa", "fwo",
+        ],
+        ["n", "k", "t", "t", "s", "z", "k", "f", "d", "l", "c", "f"],
+    ),
+    days: Locale::new_days(
+        [
+            "tsuʔntsɨ",
+            "tsuʔukpà",
+            "tsuʔughɔe",
+            "tsuʔutɔ̀mlò",
+            "tsuʔumè",
+            "tsuʔughɨ̂m",
+            "tsuʔndzɨkɔʔɔ",
+        ],
+        ["nts", "kpa", "ghɔ", "tɔm", "ume", "ghɨ", "dzk"],
+        ["nts", "kpa", "ghɔ", "tɔm", "ume", "ghɨ", "dzk"],
+        ["n", "k", "g", "t", "u", "g", "d"],
+    ),
+    day_periods: Locale::new_day_periods(["a.g", "a.k"], ["a.g", "a.k"], ["a.g", "a.k"]),
+    eras: Locale::new_eras("SK", "BK"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ak` locale.
+pub static AK: Locale<'static> = Locale {
+    lang: "ak",
+    dates: Locale::new_patterns("EEE, d, MMMM, y", "d, MMMM, y", "d, MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ɔpɛpɔn",
+            "Ɔgyefoɔ",
+            "Ɔbɛnem",
+            "Oforisuo",
+            "Kɔtɔnimma",
+            "Ayɛwohomumu",
+            "Kutawonsa",
+            "Ɔsanaa",
+            "Ɛbɔ",
+            "Ahinime",
+            "Obubuo",
+            "Ɔpɛnimma",
+        ],
+        [
+            "Ɔpɛpɔn",
+            "Ɔgyefoɔ",
+            "Ɔbɛnem",
+            "Oforisuo",
+            "Kɔtɔnimma",
+            "Ayɛwohomumu",
+            "Kutawonsa",
+            "Ɔsanaa",
+            "Ɛbɔ",
+            "Ahinime",
+            "Obubuo",
+            "Ɔpɛnimma",
+        ],
+        [
+            "Ɔp", "Ɔg", "Ɔb", "O", "K", "A", "Ku", "A", "S", "O", "N", "D",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sun", "Dwoada", "Benada", "Wukuada", "Yawoada", "Fiada", "Memeneda",
+        ],
+        ["Kwa", "Dwo", "Ben", "Wuk", "Yaw", "Fia", "Mem"],
+        ["Kwa", "Dwo", "Ben", "Wuk", "Yaw", "Fia", "Mem"],
+        ["K", "D", "B", "W", "Y", "F", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AN", "ANW"], ["AN", "ANW"], ["an", "anw"]),
+    eras: Locale::new_eras("AK", "KE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `am` locale.
+pub static AM: Locale<'static> = Locale {
+    lang: "am",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ጃንዋሪ",
+            "ፌብሩዋሪ",
+            "ማርች",
+            "ኤፕሪል",
+            "ሜይ",
+            "ጁን",
+            "ጁላይ",
+            "ኦገስት",
+            "ሴፕቴምበር",
+            "ኦክቶበር",
+            "ኖቬምበር",
+            "ዲሴምበር",
+        ],
+        [
+            "ጃን",
+            "ፌብ",
+            "ማርች",
+            "ኤፕሪ",
+            "ሜይ",
+            "ጁን",
+            "ጁላይ",
+            "ኦገስ",
+            "ሴፕቴ",
+            "ኦክቶ",
+            "ኖቬም",
+            "ዲሴም",
+        ],
+        ["ጃ", "ፌ", "ማ", "ኤ", "ሜ", "ጁ", "ጁ", "ኦ", "ሴ", "ኦ", "ኖ", "ዲ"],
+    ),
+    days: Locale::new_days(
+        ["እሑድ", "ሰኞ", "ማክሰኞ", "ረቡዕ", "ሐሙስ", "ዓርብ", "ቅዳሜ"],
+        ["እሑድ", "ሰኞ", "ማክሰ", "ረቡዕ", "ሐሙስ", "ዓርብ", "ቅዳሜ"],
+        ["እ", "ሰ", "ማ", "ረ", "ሐ", "ዓ", "ቅ"],
+        ["እ", "ሰ", "ማ", "ረ", "ሐ", "ዓ", "ቅ"],
+    ),
+    day_periods: Locale::new_day_periods(["ጥዋት", "ከሰዓት"], ["ጥዋት", "ከሰዓት"], ["ጠ", "ከ"]),
+    eras: Locale::new_eras("ዓ/ዓ", "ዓ/ም"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `an` locale.
+pub static AN: Locale<'static> = Locale {
+    lang: "an",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ann` locale.
+pub static ANN: Locale<'static> = Locale {
+    lang: "ann",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `apc` locale.
+pub static APC: Locale<'static> = Locale {
+    lang: "apc",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar` locale.
+pub static AR: Locale<'static> = Locale {
+    lang: "ar",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-AE` locale.
+pub static AR_AE: Locale<'static> = Locale {
+    lang: "ar-AE",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-BH` locale.
+pub static AR_BH: Locale<'static> = Locale {
+    lang: "ar-BH",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-DJ` locale.
+pub static AR_DJ: Locale<'static> = Locale {
+    lang: "ar-DJ",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-DZ` locale.
+pub static AR_DZ: Locale<'static> = Locale {
+    lang: "ar-DZ",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "جانفي",
+            "فيفري",
+            "مارس",
+            "أفريل",
+            "ماي",
+            "جوان",
+            "جويلية",
+            "أوت",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "جانفي",
+            "فيفري",
+            "مارس",
+            "أفريل",
+            "ماي",
+            "جوان",
+            "جويلية",
+            "أوت",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ج", "ف", "م", "أ", "م", "ج", "ج", "أ", "س", "أ", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-EG` locale.
+pub static AR_EG: Locale<'static> = Locale {
+    lang: "ar-EG",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-EH` locale.
+pub static AR_EH: Locale<'static> = Locale {
+    lang: "ar-EH",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-ER` locale.
+pub static AR_ER: Locale<'static> = Locale {
+    lang: "ar-ER",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-IL` locale.
+pub static AR_IL: Locale<'static> = Locale {
+    lang: "ar-IL",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-IQ` locale.
+pub static AR_IQ: Locale<'static> = Locale {
+    lang: "ar-IQ",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        ["ك", "ش", "آ", "ن", "أ", "ح", "ت", "آ", "أ", "ت", "ت", "ك"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-JO` locale.
+pub static AR_JO: Locale<'static> = Locale {
+    lang: "ar-JO",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        ["ك", "ش", "آ", "ن", "أ", "ح", "ت", "آ", "أ", "ت", "ت", "ك"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-KM` locale.
+pub static AR_KM: Locale<'static> = Locale {
+    lang: "ar-KM",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-KW` locale.
+pub static AR_KW: Locale<'static> = Locale {
+    lang: "ar-KW",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-LB` locale.
+pub static AR_LB: Locale<'static> = Locale {
+    lang: "ar-LB",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        ["ك", "ش", "آ", "ن", "أ", "ح", "ت", "آ", "أ", "ت", "ت", "ك"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-LY` locale.
+pub static AR_LY: Locale<'static> = Locale {
+    lang: "ar-LY",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-MA` locale.
+pub static AR_MA: Locale<'static> = Locale {
+    lang: "ar-MA",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "ماي",
+            "يونيو",
+            "يوليوز",
+            "غشت",
+            "شتنبر",
+            "أكتوبر",
+            "نونبر",
+            "دجنبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "ماي",
+            "يونيو",
+            "يوليوز",
+            "غشت",
+            "شتنبر",
+            "أكتوبر",
+            "نونبر",
+            "دجنبر",
+        ],
+        ["ي", "ف", "م", "أ", "م", "ن", "ل", "غ", "ش", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-MR` locale.
+pub static AR_MR: Locale<'static> = Locale {
+    lang: "ar-MR",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "إبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغشت",
+            "شتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "دجمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "إبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغشت",
+            "شتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "دجمبر",
+        ],
+        ["ي", "ف", "م", "إ", "و", "ن", "ل", "غ", "ش", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-OM` locale.
+pub static AR_OM: Locale<'static> = Locale {
+    lang: "ar-OM",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-PS` locale.
+pub static AR_PS: Locale<'static> = Locale {
+    lang: "ar-PS",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        ["ك", "ش", "آ", "ن", "أ", "ح", "ت", "آ", "أ", "ت", "ت", "ك"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-QA` locale.
+pub static AR_QA: Locale<'static> = Locale {
+    lang: "ar-QA",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-SA` locale.
+pub static AR_SA: Locale<'static> = Locale {
+    lang: "ar-SA",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-SD` locale.
+pub static AR_SD: Locale<'static> = Locale {
+    lang: "ar-SD",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-SO` locale.
+pub static AR_SO: Locale<'static> = Locale {
+    lang: "ar-SO",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-SS` locale.
+pub static AR_SS: Locale<'static> = Locale {
+    lang: "ar-SS",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-SY` locale.
+pub static AR_SY: Locale<'static> = Locale {
+    lang: "ar-SY",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        [
+            "كانون الثاني",
+            "شباط",
+            "آذار",
+            "نيسان",
+            "أيار",
+            "حزيران",
+            "تموز",
+            "آب",
+            "أيلول",
+            "تشرين الأول",
+            "تشرين الثاني",
+            "كانون الأول",
+        ],
+        ["ك", "ش", "آ", "ن", "أ", "ح", "ت", "آ", "أ", "ت", "ت", "ك"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-TD` locale.
+pub static AR_TD: Locale<'static> = Locale {
+    lang: "ar-TD",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ar-TN` locale.
+pub static AR_TN: Locale<'static> = Locale {
+    lang: "ar-TN",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "جانفي",
+            "فيفري",
+            "مارس",
+            "أفريل",
+            "ماي",
+            "جوان",
+            "جويلية",
+            "أوت",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "جانفي",
+            "فيفري",
+            "مارس",
+            "أفريل",
+            "ماي",
+            "جوان",
+            "جويلية",
+            "أوت",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ج", "ف", "م", "أ", "م", "ج", "ج", "أ", "س", "أ", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ar-YE` locale.
+pub static AR_YE: Locale<'static> = Locale {
+    lang: "ar-YE",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd‏/MM‏/y", "d‏/M‏/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        [
+            "يناير",
+            "فبراير",
+            "مارس",
+            "أبريل",
+            "مايو",
+            "يونيو",
+            "يوليو",
+            "أغسطس",
+            "سبتمبر",
+            "أكتوبر",
+            "نوفمبر",
+            "ديسمبر",
+        ],
+        ["ي", "ف", "م", "أ", "و", "ن", "ل", "غ", "س", "ك", "ب", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        [
+            "الأحد",
+            "الاثنين",
+            "الثلاثاء",
+            "الأربعاء",
+            "الخميس",
+            "الجمعة",
+            "السبت",
+        ],
+        ["أحد", "إثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت"],
+        ["ح", "ن", "ث", "ر", "خ", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ص", "م"], ["ص", "م"], ["ص", "م"]),
+    eras: Locale::new_eras("ق.م", "م"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `arn` locale.
+pub static ARN: Locale<'static> = Locale {
+    lang: "arn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `as` locale.
+pub static AS: Locale<'static> = Locale {
+    lang: "as",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "dd-MM-y", "d-M-y"),
+    times: Locale::new_patterns("a h.mm.ss zzzz", "a h.mm.ss z", "a h.mm.ss", "a h.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "জানুৱাৰী",
+            "ফেব্ৰুৱাৰী",
+            "মাৰ্চ",
+            "এপ্ৰিল",
+            "মে’",
+            "জুন",
+            "জুলাই",
+            "আগষ্ট",
+            "ছেপ্তেম্বৰ",
+            "অক্টোবৰ",
+            "নৱেম্বৰ",
+            "ডিচেম্বৰ",
+        ],
+        [
+            "জানু",
+            "ফেব্ৰু",
+            "মাৰ্চ",
+            "এপ্ৰিল",
+            "মে’",
+            "জুন",
+            "জুলাই",
+            "আগ",
+            "ছেপ্তে",
+            "অক্টো",
+            "নৱে",
+            "ডিচে",
+        ],
+        ["জ", "ফ", "ম", "এ", "ম", "জ", "জ", "আ", "ছ", "অ", "ন", "ড"],
+    ),
+    days: Locale::new_days(
+        [
+            "দেওবাৰ",
+            "সোমবাৰ",
+            "মঙ্গলবাৰ",
+            "বুধবাৰ",
+            "বৃহস্পতিবাৰ",
+            "শুক্ৰবাৰ",
+            "শনিবাৰ",
+        ],
+        ["দেও", "সোম", "মঙ্গল", "বুধ", "বৃহ", "শুক্ৰ", "শনি"],
+        ["দেও", "সোম", "মঙ্গল", "বুধ", "বৃহ", "শুক্ৰ", "শনি"],
+        ["দ", "স", "ম", "ব", "ব", "শ", "শ"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["পূৰ্বাহ্ন", "অপৰাহ্ন"],
+        ["পূৰ্বাহ্ন", "অপৰাহ্ন"],
+        ["পূৰ্বাহ্ন", "অপৰাহ্ন"],
+    ),
+    eras: Locale::new_eras("খ্ৰীঃ পূঃ", "খ্ৰীঃ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `asa` locale.
+pub static ASA: Locale<'static> = Locale {
+    lang: "asa",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Ijm", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Ijm", "Jmo"],
+        ["J", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["icheheavo", "ichamthi"],
+        ["icheheavo", "ichamthi"],
+        ["icheheavo", "ichamthi"],
+    ),
+    eras: Locale::new_eras("KM", "BM"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ast` locale.
+pub static AST: Locale<'static> = Locale {
+    lang: "ast",
+    dates: Locale::new_patterns("EEEE, d MMMM 'de' y", "d MMMM 'de' y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de xineru",
+            "de febreru",
+            "de marzu",
+            "d’abril",
+            "de mayu",
+            "de xunu",
+            "de xunetu",
+            "d’agostu",
+            "de setiembre",
+            "d’ochobre",
+            "de payares",
+            "d’avientu",
+        ],
+        [
+            "xin", "feb", "mar", "abr", "may", "xun", "xnt", "ago", "set", "och", "pay", "avi",
+        ],
+        ["X", "F", "M", "A", "M", "X", "X", "A", "S", "O", "P", "A"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingu",
+            "llunes",
+            "martes",
+            "miércoles",
+            "xueves",
+            "vienres",
+            "sábadu",
+        ],
+        ["dom", "llu", "mar", "mié", "xue", "vie", "sáb"],
+        ["do", "ll", "ma", "mi", "xu", "vi", "sá"],
+        ["D", "L", "M", "M", "X", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["de la mañana", "de la tarde"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("e.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `az` locale.
+pub static AZ: Locale<'static> = Locale {
+    lang: "az",
+    dates: Locale::new_patterns("d MMMM y, EEEE", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avqust", "sentyabr",
+            "oktyabr", "noyabr", "dekabr",
+        ],
+        [
+            "yan", "fev", "mar", "apr", "may", "iyn", "iyl", "avq", "sen", "okt", "noy", "dek",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "bazar",
+            "bazar ertəsi",
+            "çərşənbə axşamı",
+            "çərşənbə",
+            "cümə axşamı",
+            "cümə",
+            "şənbə",
+        ],
+        ["B.", "B.e.", "Ç.a.", "Ç.", "C.a.", "C.", "Ş."],
+        ["B.", "B.E.", "Ç.A.", "Ç.", "C.A.", "C.", "Ş."],
+        ["7", "1", "2", "3", "4", "5", "6"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("e.ə.", "y.e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `az-Arab` locale.
+pub static AZ_ARAB: Locale<'static> = Locale {
+    lang: "az-Arab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `az-Arab-IQ` locale.
+pub static AZ_ARAB_IQ: Locale<'static> = Locale {
+    lang: "az-Arab-IQ",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `az-Arab-TR` locale.
+pub static AZ_ARAB_TR: Locale<'static> = Locale {
+    lang: "az-Arab-TR",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `az-Cyrl` locale.
+pub static AZ_CYRL: Locale<'static> = Locale {
+    lang: "az-Cyrl",
+    dates: Locale::new_patterns("d MMMM y, EEEE", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јанвар",
+            "феврал",
+            "март",
+            "апрел",
+            "май",
+            "ијун",
+            "ијул",
+            "август",
+            "сентјабр",
+            "октјабр",
+            "нојабр",
+            "декабр",
+        ],
+        [
+            "јан", "фев", "мар", "апр", "май", "ијн", "ијл", "авг", "сен", "окт", "ној", "дек",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "базар",
+            "базар ертәси",
+            "чәршәнбә ахшамы",
+            "чәршәнбә",
+            "ҹүмә ахшамы",
+            "ҹүмә",
+            "шәнбә",
+        ],
+        ["Б.", "Б.Е.", "Ч.А.", "Ч.", "Ҹ.А.", "Ҹ.", "Ш."],
+        ["Б.", "Б.Е.", "Ч.А.", "Ч.", "Ҹ.А.", "Ҹ.", "Ш."],
+        ["7", "1", "2", "3", "4", "5", "6"],
+    ),
+    day_periods: Locale::new_day_periods(["АМ", "ПМ"], ["АМ", "ПМ"], ["а", "п"]),
+    eras: Locale::new_eras("е.ә.", "ј.е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `az-Latn` locale.
+pub static AZ_LATN: Locale<'static> = Locale {
+    lang: "az-Latn",
+    dates: Locale::new_patterns("d MMMM y, EEEE", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avqust", "sentyabr",
+            "oktyabr", "noyabr", "dekabr",
+        ],
+        [
+            "yan", "fev", "mar", "apr", "may", "iyn", "iyl", "avq", "sen", "okt", "noy", "dek",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "bazar",
+            "bazar ertəsi",
+            "çərşənbə axşamı",
+            "çərşənbə",
+            "cümə axşamı",
+            "cümə",
+            "şənbə",
+        ],
+        ["B.", "B.e.", "Ç.a.", "Ç.", "C.a.", "C.", "Ş."],
+        ["B.", "B.E.", "Ç.A.", "Ç.", "C.A.", "C.", "Ş."],
+        ["7", "1", "2", "3", "4", "5", "6"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("e.ə.", "y.e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ba` locale.
+pub static BA: Locale<'static> = Locale {
+    lang: "ba",
+    dates: Locale::new_patterns(
+        "d MMMM, EEEE, y 'й'.",
+        "d MMMM, y 'й'.",
+        "d MMM, y 'й'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ғинуар",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "ғин.",
+            "фев.",
+            "мар.",
+            "апр.",
+            "май",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Ғ", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "йәкшәмбе",
+            "дүшәмбе",
+            "шишәмбе",
+            "шаршамбы",
+            "кесаҙна",
+            "йома",
+            "шәмбе",
+        ],
+        ["йәк.", "дүш.", "шиш.", "шар.", "кес.", "йом.", "шәм."],
+        ["йш", "дш", "шш", "шр", "кс", "йм", "шб"],
+        ["Й", "Д", "Ш", "Ш", "К", "Й", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б.э.т.", "б.э."),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `bal` locale.
+pub static BAL: Locale<'static> = Locale {
+    lang: "bal",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bal-Arab` locale.
+pub static BAL_ARAB: Locale<'static> = Locale {
+    lang: "bal-Arab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bal-Latn` locale.
+pub static BAL_LATN: Locale<'static> = Locale {
+    lang: "bal-Latn",
+    dates: Locale::new_patterns("dd,MM,y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("hh:mm:ss a zzzz", "hh:mm:ss a zzz", "hh:mm:ss a", "hh:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janwari", "Parwari", "Márch", "Aprél", "Mai", "Jun", "Jólái", "Agast", "Satambar",
+            "Aktubar", "Nawambar", "Dasambar",
+        ],
+        [
+            "Jan", "Par", "Már", "Apr", "Mai", "Jun", "Jól", "Aga", "Sat", "Akt", "Naw", "Das",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Yakshambeh",
+            "Doshambeh",
+            "Sayshambeh",
+            "Chárshambeh",
+            "Panchshambeh",
+            "Jomah",
+            "Shambeh",
+        ],
+        ["Yak", "Do", "Say", "Chá", "Pan", "Jom", "Sha"],
+        ["Yak", "Do", "Say", "Chá", "Pan", "Jom", "Sha"],
+        ["Y", "D", "S", "Ch", "P", "J", "Sh"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["am", "pm"]),
+    eras: Locale::new_eras("PM", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bas` locale.
+pub static BAS: Locale<'static> = Locale {
+    lang: "bas",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Kɔndɔŋ",
+            "Màcɛ̂l",
+            "Màtùmb",
+            "Màtop",
+            "M̀puyɛ",
+            "Hìlòndɛ̀",
+            "Njèbà",
+            "Hìkaŋ",
+            "Dìpɔ̀s",
+            "Bìòôm",
+            "Màyɛsèp",
+            "Lìbuy li ńyèe",
+        ],
+        [
+            "kɔn", "mac", "mat", "mto", "mpu", "hil", "nje", "hik", "dip", "bio", "may", "liɓ",
+        ],
+        ["k", "m", "m", "m", "m", "h", "n", "h", "d", "b", "m", "l"],
+    ),
+    days: Locale::new_days(
+        [
+            "ŋgwà nɔ̂y",
+            "ŋgwà njaŋgumba",
+            "ŋgwà ûm",
+            "ŋgwà ŋgê",
+            "ŋgwà mbɔk",
+            "ŋgwà kɔɔ",
+            "ŋgwà jôn",
+        ],
+        ["nɔy", "nja", "uum", "ŋge", "mbɔ", "kɔɔ", "jon"],
+        ["nɔy", "nja", "uum", "ŋge", "mbɔ", "kɔɔ", "jon"],
+        ["n", "n", "u", "ŋ", "m", "k", "j"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["I bikɛ̂glà", "I ɓugajɔp"],
+        ["I bikɛ̂glà", "I ɓugajɔp"],
+        ["I bikɛ̂glà", "I ɓugajɔp"],
+    ),
+    eras: Locale::new_eras("b.Y.K", "m.Y.K"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `be` locale.
+pub static BE: Locale<'static> = Locale {
+    lang: "be",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "d.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss, zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "студзеня",
+            "лютага",
+            "сакавіка",
+            "красавіка",
+            "мая",
+            "чэрвеня",
+            "ліпеня",
+            "жніўня",
+            "верасня",
+            "кастрычніка",
+            "лістапада",
+            "снежня",
+        ],
+        [
+            "сту", "лют", "сак", "кра", "мая", "чэр", "ліп", "жні", "вер", "кас", "ліс", "сне",
+        ],
+        ["с", "л", "с", "к", "м", "ч", "л", "ж", "в", "к", "л", "с"],
+    ),
+    days: Locale::new_days(
+        [
+            "нядзеля",
+            "панядзелак",
+            "аўторак",
+            "серада",
+            "чацвер",
+            "пятніца",
+            "субота",
+        ],
+        ["нд", "пн", "аў", "ср", "чц", "пт", "сб"],
+        ["нд", "пн", "аў", "ср", "чц", "пт", "сб"],
+        ["н", "п", "а", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("да н.э.", "н.э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `be-tarask` locale.
+pub static BE_TARASK: Locale<'static> = Locale {
+    lang: "be-tarask",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "d.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss, zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "студзеня",
+            "лютага",
+            "сакавіка",
+            "красавіка",
+            "мая",
+            "чэрвеня",
+            "ліпеня",
+            "жніўня",
+            "верасня",
+            "кастрычніка",
+            "лістапада",
+            "снежня",
+        ],
+        [
+            "сту", "лют", "сак", "кра", "мая", "чэр", "ліп", "жні", "вер", "кас", "ліс", "сне",
+        ],
+        ["с", "л", "с", "к", "м", "ч", "л", "ж", "в", "к", "л", "с"],
+    ),
+    days: Locale::new_days(
+        [
+            "нядзеля",
+            "панядзелак",
+            "аўторак",
+            "серада",
+            "чацвер",
+            "пятніца",
+            "субота",
+        ],
+        ["нд", "пн", "аў", "ср", "чц", "пт", "сб"],
+        ["нд", "пн", "аў", "ср", "чц", "пт", "сб"],
+        ["н", "п", "а", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("да н.э.", "н.э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `bem` locale.
+pub static BEM: Locale<'static> = Locale {
+    lang: "bem",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Epreo", "Mei", "Juni", "Julai", "Ogasti", "Septemba",
+            "Oktoba", "Novemba", "Disemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Epr", "Mei", "Jun", "Jul", "Oga", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "E", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Pa Mulungu",
+            "Palichimo",
+            "Palichibuli",
+            "Palichitatu",
+            "Palichine",
+            "Palichisano",
+            "Pachibelushi",
+        ],
+        [
+            "Pa Mulungu",
+            "Palichimo",
+            "Palichibuli",
+            "Palichitatu",
+            "Palichine",
+            "Palichisano",
+            "Pachibelushi",
+        ],
+        [
+            "Pa Mulungu",
+            "Palichimo",
+            "Palichibuli",
+            "Palichitatu",
+            "Palichine",
+            "Palichisano",
+            "Pachibelushi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["uluchelo", "akasuba"],
+        ["uluchelo", "akasuba"],
+        ["uluchelo", "akasuba"],
+    ),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bew` locale.
+pub static BEW: Locale<'static> = Locale {
+    lang: "bew",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bez` locale.
+pub static BEZ: Locale<'static> = Locale {
+    lang: "bez",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "pa mwedzi gwa hutala",
+            "pa mwedzi gwa wuvili",
+            "pa mwedzi gwa wudatu",
+            "pa mwedzi gwa wutai",
+            "pa mwedzi gwa wuhanu",
+            "pa mwedzi gwa sita",
+            "pa mwedzi gwa saba",
+            "pa mwedzi gwa nane",
+            "pa mwedzi gwa tisa",
+            "pa mwedzi gwa kumi",
+            "pa mwedzi gwa kumi na moja",
+            "pa mwedzi gwa kumi na mbili",
+        ],
+        [
+            "Hut", "Vil", "Dat", "Tai", "Han", "Sit", "Sab", "Nan", "Tis", "Kum", "Kmj", "Kmb",
+        ],
+        ["H", "V", "D", "T", "H", "S", "S", "N", "T", "K", "K", "K"],
+    ),
+    days: Locale::new_days(
+        [
+            "pa mulungu",
+            "pa shahuviluha",
+            "pa hivili",
+            "pa hidatu",
+            "pa hitayi",
+            "pa hihanu",
+            "pa shahulembela",
+        ],
+        ["Mul", "Vil", "Hiv", "Hid", "Hit", "Hih", "Lem"],
+        ["Mul", "Vil", "Hiv", "Hid", "Hit", "Hih", "Lem"],
+        ["M", "J", "H", "H", "H", "W", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["pamilau", "pamunyi"],
+        ["pamilau", "pamunyi"],
+        ["pamilau", "pamunyi"],
+    ),
+    eras: Locale::new_eras("KM", "BM"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bg` locale.
+pub static BG: Locale<'static> = Locale {
+    lang: "bg",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d.MM.y 'г'.",
+        "d.MM.yy 'г'.",
+    ),
+    times: Locale::new_patterns("H:mm:ss 'ч'. zzzz", "H:mm:ss 'ч'. z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "януари",
+            "февруари",
+            "март",
+            "април",
+            "май",
+            "юни",
+            "юли",
+            "август",
+            "септември",
+            "октомври",
+            "ноември",
+            "декември",
+        ],
+        [
+            "яну", "фев", "март", "апр", "май", "юни", "юли", "авг", "сеп", "окт", "ное", "дек",
+        ],
+        ["я", "ф", "м", "а", "м", "ю", "ю", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "неделя",
+            "понеделник",
+            "вторник",
+            "сряда",
+            "четвъртък",
+            "петък",
+            "събота",
+        ],
+        ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["н", "п", "в", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["пр.об.", "сл.об."], ["am", "pm"], ["am", "pm"]),
+    eras: Locale::new_eras("пр.Хр.", "сл.Хр."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `bgc` locale.
+pub static BGC: Locale<'static> = Locale {
+    lang: "bgc",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवम्बर",
+            "दिसंबर",
+        ],
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवम्बर",
+            "दिसंबर",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ऐतवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "बृहस्पतवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        [
+            "ऐतवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "बृहस्पतवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        [
+            "ऐतवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "बृहस्पतवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["तडके", "सांझ"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bgn` locale.
+pub static BGN: Locale<'static> = Locale {
+    lang: "bgn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '٫',
+    group: '،',
+};
+/// Data for `bgn-AE` locale.
+pub static BGN_AE: Locale<'static> = Locale {
+    lang: "bgn-AE",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '٫',
+    group: '،',
+};
+/// Data for `bgn-AF` locale.
+pub static BGN_AF: Locale<'static> = Locale {
+    lang: "bgn-AF",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '٫',
+    group: '،',
+};
+/// Data for `bgn-IR` locale.
+pub static BGN_IR: Locale<'static> = Locale {
+    lang: "bgn-IR",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '٫',
+    group: '،',
+};
+/// Data for `bgn-OM` locale.
+pub static BGN_OM: Locale<'static> = Locale {
+    lang: "bgn-OM",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '٫',
+    group: '،',
+};
+/// Data for `bho` locale.
+pub static BHO: Locale<'static> = Locale {
+    lang: "bho",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रबीबार",
+            "सोमबार",
+            "मंगलबार",
+            "बुधबार",
+            "बृहस्पतिबार",
+            "शुक्रबार",
+            "सनीचर",
+        ],
+        [
+            "रबीबार",
+            "सोमबार",
+            "मंगलबार",
+            "बुधबार",
+            "बृहस्पतिबार",
+            "शुक्रबार",
+            "सनीचर",
+        ],
+        [
+            "रबीबार",
+            "सोमबार",
+            "मंगलबार",
+            "बुधबार",
+            "बृहस्पतिबार",
+            "शुक्रबार",
+            "सनीचर",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["भोर", "सांझ"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `blo` locale.
+pub static BLO: Locale<'static> = Locale {
+    lang: "blo",
+    dates: Locale::new_patterns("EEEE, MMMM d/y", "y MMMM d", "MMM d/y", "M/d/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ɩjikawǝrka kaŋɔrɔ",
+            "ɩjikpaka kaŋɔrɔ",
+            "arɛ́cika kaŋɔrɔ",
+            "njɩbɔ nɖʊka kaŋɔrɔ",
+            "acafʊnɖuka kaŋɔrɔ",
+            "anɔɔɖuka kaŋɔrɔ",
+            "alàlaka kaŋɔrɔ",
+            "ɩjikǝuka kaŋɔrɔ",
+            "abofʊmka kaŋɔrɔ",
+            "ɩjicimka kaŋɔrɔ",
+            "acapomka kaŋɔrɔ",
+            "anɔɔbʊnka kaŋɔrɔ",
+        ],
+        [
+            "kaw", "kpa", "ci", "ɖʊ", "ɖu5", "ɖu6", "la", "kǝu", "fʊm", "cim", "pom", "bʊn",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "alahaɖɩ",
+            "aɖɩtɛnɛɛ",
+            "atalaata",
+            "alaarba",
+            "alaamɩshɩ",
+            "arɩsǝma",
+            "asiibi",
+        ],
+        ["alah", "aɖɩt", "atal", "alar", "alam", "arɩs", "asib"],
+        ["alh", "aɖt", "atl", "alr", "alm", "ars", "asb"],
+        ["lh", "ɖt", "tl", "lr", "lm", "rs", "sb"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ʊshilè kʊboɖu", "ʊshilè kʊsasʊ"],
+        ["1ka", "2ja"],
+        ["1ka", "2ja"],
+    ),
+    eras: Locale::new_eras("naaBYŊAƖ", "AƖAK"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `blt` locale.
+pub static BLT: Locale<'static> = Locale {
+    lang: "blt",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bm` locale.
+pub static BM: Locale<'static> = Locale {
+    lang: "bm",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "zanwuye",
+            "feburuye",
+            "marisi",
+            "awirili",
+            "mɛ",
+            "zuwɛn",
+            "zuluye",
+            "uti",
+            "sɛtanburu",
+            "ɔkutɔburu",
+            "nowanburu",
+            "desanburu",
+        ],
+        [
+            "zan", "feb", "mar", "awi", "mɛ", "zuw", "zul", "uti", "sɛt", "ɔku", "now", "des",
+        ],
+        ["Z", "F", "M", "A", "M", "Z", "Z", "U", "S", "Ɔ", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "kari", "ntɛnɛ", "tarata", "araba", "alamisa", "juma", "sibiri",
+        ],
+        ["kar", "ntɛ", "tar", "ara", "ala", "jum", "sib"],
+        ["kar", "ntɛ", "tar", "ara", "ala", "jum", "sib"],
+        ["K", "N", "T", "A", "A", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("J.-C. ɲɛ", "ni J.-C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bm-Nkoo` locale.
+pub static BM_NKOO: Locale<'static> = Locale {
+    lang: "bm-Nkoo",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bn` locale.
+pub static BN: Locale<'static> = Locale {
+    lang: "bn",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "জানুয়ারি",
+            "ফেব্রুয়ারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "আগস্ট",
+            "সেপ্টেম্বর",
+            "অক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জানু",
+            "ফেব",
+            "মার্চ",
+            "এপ্রি",
+            "মে",
+            "জুন",
+            "জুল",
+            "আগ",
+            "সেপ",
+            "অক্টো",
+            "নভে",
+            "ডিসে",
+        ],
+        [
+            "জা",
+            "ফে",
+            "মা",
+            "এ",
+            "মে",
+            "জুন",
+            "জু",
+            "আ",
+            "সে",
+            "অ",
+            "ন",
+            "ডি",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "রবিবার",
+            "সোমবার",
+            "মঙ্গলবার",
+            "বুধবার",
+            "বৃহস্পতিবার",
+            "শুক্রবার",
+            "শনিবার",
+        ],
+        ["রবি", "সোম", "মঙ্গল", "বুধ", "বৃহস্পতি", "শুক্র", "শনি"],
+        ["রঃ", "সোঃ", "মঃ", "বুঃ", "বৃঃ", "শুঃ", "শনি"],
+        ["র", "সো", "ম", "বু", "বৃ", "শু", "শ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("খ্রিস্টপূর্ব", "খৃষ্টাব্দ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bn-IN` locale.
+pub static BN_IN: Locale<'static> = Locale {
+    lang: "bn-IN",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "জানুয়ারি",
+            "ফেব্রুয়ারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "আগস্ট",
+            "সেপ্টেম্বর",
+            "অক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জানু",
+            "ফেব",
+            "মার্চ",
+            "এপ্রি",
+            "মে",
+            "জুন",
+            "জুল",
+            "আগ",
+            "সেপ্টেঃ",
+            "অক্টোঃ",
+            "নভেঃ",
+            "ডিসেঃ",
+        ],
+        [
+            "জা",
+            "ফে",
+            "মা",
+            "এ",
+            "মে",
+            "জুন",
+            "জুল",
+            "আ",
+            "সে",
+            "অ",
+            "ন",
+            "ডি",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "রবিবার",
+            "সোমবার",
+            "মঙ্গলবার",
+            "বুধবার",
+            "বৃহস্পতিবার",
+            "শুক্রবার",
+            "শনিবার",
+        ],
+        ["রবি", "সোম", "মঙ্গল", "বুধ", "বৃহস্পতি", "শুক্র", "শনি"],
+        ["রঃ", "সোঃ", "মঃ", "বুঃ", "বৃঃ", "শুঃ", "শঃ"],
+        ["র", "সো", "ম", "বু", "বৃ", "শু", "শ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("খ্রিঃপূঃ", "খ্রিঃ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bo` locale.
+pub static BO: Locale<'static> = Locale {
+    lang: "bo",
+    dates: Locale::new_patterns(
+        "y MMMMའི་ཚེས་d, EEEE",
+        "སྤྱི་ལོ་y MMMMའི་ཚེས་d",
+        "y ལོའི་MMMཚེས་d",
+        "y-MM-dd",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ཟླ་བ་དང་པོ",
+            "ཟླ་བ་གཉིས་པ",
+            "ཟླ་བ་གསུམ་པ",
+            "ཟླ་བ་བཞི་པ",
+            "ཟླ་བ་ལྔ་པ",
+            "ཟླ་བ་དྲུག་པ",
+            "ཟླ་བ་བདུན་པ",
+            "ཟླ་བ་བརྒྱད་པ",
+            "ཟླ་བ་དགུ་པ",
+            "ཟླ་བ་བཅུ་པ",
+            "ཟླ་བ་བཅུ་གཅིག་པ",
+            "ཟླ་བ་བཅུ་གཉིས་པ",
+        ],
+        [
+            "ཟླ་༡",
+            "ཟླ་༢",
+            "ཟླ་༣",
+            "ཟླ་༤",
+            "ཟླ་༥",
+            "ཟླ་༦",
+            "ཟླ་༧",
+            "ཟླ་༨",
+            "ཟླ་༩",
+            "ཟླ་༡༠",
+            "ཟླ་༡༡",
+            "ཟླ་༡༢",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "གཟའ་ཉི་མ་",
+            "གཟའ་ཟླ་བ་",
+            "གཟའ་མིག་དམར་",
+            "གཟའ་ལྷག་པ་",
+            "གཟའ་ཕུར་བུ་",
+            "གཟའ་པ་སངས་",
+            "གཟའ་སྤེན་པ་",
+        ],
+        [
+            "ཉི་མ་",
+            "ཟླ་བ་",
+            "མིག་དམར་",
+            "ལྷག་པ་",
+            "ཕུར་བུ་",
+            "པ་སངས་",
+            "སྤེན་པ་",
+        ],
+        [
+            "ཉི་མ་",
+            "ཟླ་བ་",
+            "མིག་དམར་",
+            "ལྷག་པ་",
+            "ཕུར་བུ་",
+            "པ་སངས་",
+            "སྤེན་པ་",
+        ],
+        ["ཉི", "ཟླ", "མིག", "ལྷག", "ཕུར", "སངས", "སྤེན"],
+    ),
+    day_periods: Locale::new_day_periods(["སྔ་དྲོ་", "ཕྱི་དྲོ་"], ["སྔ་དྲོ་", "ཕྱི་དྲོ་"], ["སྔ་དྲོ་", "ཕྱི་དྲོ་"]),
+    eras: Locale::new_eras("སྤྱི་ལོ་སྔོན་", "སྤྱི་ལོ་"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bo-IN` locale.
+pub static BO_IN: Locale<'static> = Locale {
+    lang: "bo-IN",
+    dates: Locale::new_patterns(
+        "y MMMMའི་ཚེས་d, EEEE",
+        "སྤྱི་ལོ་y MMMMའི་ཚེས་d",
+        "y ལོའི་MMMཚེས་d",
+        "y-MM-dd",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ཟླ་བ་དང་པོ",
+            "ཟླ་བ་གཉིས་པ",
+            "ཟླ་བ་གསུམ་པ",
+            "ཟླ་བ་བཞི་པ",
+            "ཟླ་བ་ལྔ་པ",
+            "ཟླ་བ་དྲུག་པ",
+            "ཟླ་བ་བདུན་པ",
+            "ཟླ་བ་བརྒྱད་པ",
+            "ཟླ་བ་དགུ་པ",
+            "ཟླ་བ་བཅུ་པ",
+            "ཟླ་བ་བཅུ་གཅིག་པ",
+            "ཟླ་བ་བཅུ་གཉིས་པ",
+        ],
+        [
+            "ཟླ་༡",
+            "ཟླ་༢",
+            "ཟླ་༣",
+            "ཟླ་༤",
+            "ཟླ་༥",
+            "ཟླ་༦",
+            "ཟླ་༧",
+            "ཟླ་༨",
+            "ཟླ་༩",
+            "ཟླ་༡༠",
+            "ཟླ་༡༡",
+            "ཟླ་༡༢",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "གཟའ་ཉི་མ་",
+            "གཟའ་ཟླ་བ་",
+            "གཟའ་མིག་དམར་",
+            "གཟའ་ལྷག་པ་",
+            "གཟའ་ཕུར་བུ་",
+            "གཟའ་པ་སངས་",
+            "གཟའ་སྤེན་པ་",
+        ],
+        [
+            "ཉི་མ་",
+            "ཟླ་བ་",
+            "མིག་དམར་",
+            "ལྷག་པ་",
+            "ཕུར་བུ་",
+            "པ་སངས་",
+            "སྤེན་པ་",
+        ],
+        [
+            "ཉི་མ་",
+            "ཟླ་བ་",
+            "མིག་དམར་",
+            "ལྷག་པ་",
+            "ཕུར་བུ་",
+            "པ་སངས་",
+            "སྤེན་པ་",
+        ],
+        ["ཉི", "ཟླ", "མིག", "ལྷག", "ཕུར", "སངས", "སྤེན"],
+    ),
+    day_periods: Locale::new_day_periods(["སྔ་དྲོ་", "ཕྱི་དྲོ་"], ["སྔ་དྲོ་", "ཕྱི་དྲོ་"], ["སྔ་དྲོ་", "ཕྱི་དྲོ་"]),
+    eras: Locale::new_eras("སྤྱི་ལོ་སྔོན་", "སྤྱི་ལོ་"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bqi` locale.
+pub static BQI: Locale<'static> = Locale {
+    lang: "bqi",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `br` locale.
+pub static BR: Locale<'static> = Locale {
+    lang: "br",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Genver",
+            "Cʼhwevrer",
+            "Meurzh",
+            "Ebrel",
+            "Mae",
+            "Mezheven",
+            "Gouere",
+            "Eost",
+            "Gwengolo",
+            "Here",
+            "Du",
+            "Kerzu",
+        ],
+        [
+            "Gen.", "Cʼhwe.", "Meur.", "Ebr.", "Mae", "Mezh.", "Goue.", "Eost", "Gwen.", "Here",
+            "Du", "Kzu.",
+        ],
+        [
+            "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sul",
+            "Lun",
+            "Meurzh",
+            "Mercʼher",
+            "Yaou",
+            "Gwener",
+            "Sadorn",
+        ],
+        ["Sul", "Lun", "Meu.", "Mer.", "Yaou", "Gwe.", "Sad."],
+        ["Sul", "Lun", "Meu.", "Mer.", "Yaou", "Gwe.", "Sad."],
+        ["Su", "L", "Mz", "Mc", "Y", "G", "Sa"],
+    ),
+    day_periods: Locale::new_day_periods(["A.M.", "G.M."], ["A.M.", "G.M."], ["am", "gm"]),
+    eras: Locale::new_eras("a-raok J.K.", "goude J.K."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `brx` locale.
+pub static BRX: Locale<'static> = Locale {
+    lang: "brx",
+    dates: Locale::new_patterns("d MMMM y, EEEE", "d MMMM, y", "d MMM, y", "dd-MM-y"),
+    times: Locale::new_patterns("a h:mm:ss zzzz", "a h:mm:ss z", "a h:mm:ss", "a नि h:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जानुवारी",
+            "फेब्रूवारी",
+            "मार्च",
+            "एप्रिल",
+            "मे",
+            "जुन",
+            "जुलाई",
+            "आगष्ट",
+            "सेप्थेम्बर",
+            "अक्ट’बर",
+            "नवेम्बर",
+            "डिसेम्बर",
+        ],
+        [
+            "जान",
+            "फेब",
+            "मार्च",
+            "एप्रि",
+            "मे",
+            "जुन",
+            "जुल",
+            "आग",
+            "सेप",
+            "अक्ट’",
+            "नवे",
+            "डिसे",
+        ],
+        ["ज", "फ", "म", "ए", "म", "ज", "ज", "आ", "स", "अ", "न", "ड"],
+    ),
+    days: Locale::new_days(
+        [
+            "रबिबार",
+            "समबार",
+            "मंगलबार",
+            "बुधबार",
+            "बिस्थिबार",
+            "सुखुरबार",
+            "सनिबार",
+        ],
+        ["रबि", "सम", "मंगल", "बुध", "बिस्थि", "सुखुर", "सनि"],
+        ["रबि", "सम", "मंगल", "बुध", "बिस्थि", "सुखुर", "सनि"],
+        ["र", "स", "मं", "बु", "बि", "सु", "स"],
+    ),
+    day_periods: Locale::new_day_periods(["फुं", "बेलासे"], ["फुं", "बेलासे"], ["फुं", "बे"]),
+    eras: Locale::new_eras("बि.सि.", "ए.दि"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bs` locale.
+pub static BS: Locale<'static> = Locale {
+    lang: "bs",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. MMM y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "august",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedjeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["N", "P", "U", "S", "Č", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["prijepodne", "popodne"],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `bs-Cyrl` locale.
+pub static BS_CYRL: Locale<'static> = Locale {
+    lang: "bs-Cyrl",
+    dates: Locale::new_patterns("EEEE, dd. MMMM y.", "dd. MMMM y.", "dd.MM.y.", "d.M.yy."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јуни",
+            "јули",
+            "аугуст",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "мар", "апр", "мај", "јун", "јул", "ауг", "сеп", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недјеља",
+            "понедјељак",
+            "уторак",
+            "сриједа",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сри", "чет", "пет", "суб"],
+        ["нед", "пон", "уто", "сри", "чет", "пет", "суб"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["прије подне", "послије подне"],
+        ["пре подне", "поподне"],
+        ["пре подне", "поподне"],
+    ),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `bs-Latn` locale.
+pub static BS_LATN: Locale<'static> = Locale {
+    lang: "bs-Latn",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. MMM y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "august",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedjeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["N", "P", "U", "S", "Č", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["prijepodne", "popodne"],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `bss` locale.
+pub static BSS: Locale<'static> = Locale {
+    lang: "bss",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `bua` locale.
+pub static BUA: Locale<'static> = Locale {
+    lang: "bua",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "нэгэдүгээр һара",
+            "хоёрдугаар һара",
+            "гурбадугаар һара",
+            "дүрбэдүгээр һара",
+            "табадугаар һара",
+            "зургадугаар һара",
+            "долодугаар һара",
+            "наймадугаар һара",
+            "юһэдүгээр һара",
+            "арбадугаар һара",
+            "арбан нэгэдүгээр һара",
+            "арбан хоёрдугаар һара",
+        ],
+        [
+            "нэгэдүгээр һара",
+            "хоёрдугаар һара",
+            "гурбадугаар һара",
+            "дүрбэдүгээр һара",
+            "табадугаар һара",
+            "зургадугаар һара",
+            "долодугаар һара",
+            "наймадугаар һара",
+            "юһэдүгээр һара",
+            "арбадугаар һара",
+            "арбан нэгэдүгээр һара",
+            "арбан хоёрдугаар һара",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "нима",
+            "дабаа",
+            "мигмар",
+            "һарба",
+            "пүрбэ",
+            "баасан",
+            "бимба",
+        ],
+        [
+            "нима",
+            "дабаа",
+            "мигмар",
+            "һарба",
+            "пүрбэ",
+            "баасан",
+            "бимба",
+        ],
+        [
+            "нима",
+            "дабаа",
+            "мигмар",
+            "һарба",
+            "пүрбэ",
+            "баасан",
+            "бимба",
+        ],
+        ["Ни", "Да", "Ми", "Һа", "Пү", "Ба", "Би"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `byn` locale.
+pub static BYN: Locale<'static> = Locale {
+    lang: "byn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ca` locale.
+pub static CA: Locale<'static> = Locale {
+    lang: "ca",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM 'del' y",
+        "d MMMM 'del' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de gener",
+            "de febrer",
+            "de març",
+            "d’abril",
+            "de maig",
+            "de juny",
+            "de juliol",
+            "d’agost",
+            "de setembre",
+            "d’octubre",
+            "de novembre",
+            "de desembre",
+        ],
+        [
+            "de gen.", "de febr.", "de març", "d’abr.", "de maig", "de juny", "de jul.", "d’ag.",
+            "de set.", "d’oct.", "de nov.", "de des.",
+        ],
+        [
+            "GN", "FB", "MÇ", "AB", "MG", "JN", "JL", "AG", "ST", "OC", "NV", "DS",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "diumenge",
+            "dilluns",
+            "dimarts",
+            "dimecres",
+            "dijous",
+            "divendres",
+            "dissabte",
+        ],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("aC", "dC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ca-AD` locale.
+pub static CA_AD: Locale<'static> = Locale {
+    lang: "ca-AD",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM 'del' y",
+        "d MMMM 'del' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de gener",
+            "de febrer",
+            "de març",
+            "d’abril",
+            "de maig",
+            "de juny",
+            "de juliol",
+            "d’agost",
+            "de setembre",
+            "d’octubre",
+            "de novembre",
+            "de desembre",
+        ],
+        [
+            "de gen.", "de febr.", "de març", "d’abr.", "de maig", "de juny", "de jul.", "d’ag.",
+            "de set.", "d’oct.", "de nov.", "de des.",
+        ],
+        [
+            "GN", "FB", "MÇ", "AB", "MG", "JN", "JL", "AG", "ST", "OC", "NV", "DS",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "diumenge",
+            "dilluns",
+            "dimarts",
+            "dimecres",
+            "dijous",
+            "divendres",
+            "dissabte",
+        ],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("aC", "dC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ca-ES-valencia` locale.
+pub static CA_ES_VALENCIA: Locale<'static> = Locale {
+    lang: "ca-ES-valencia",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM 'del' y",
+        "d MMMM 'del' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de gener",
+            "de febrer",
+            "de març",
+            "d’abril",
+            "de maig",
+            "de juny",
+            "de juliol",
+            "d’agost",
+            "de setembre",
+            "d’octubre",
+            "de novembre",
+            "de desembre",
+        ],
+        [
+            "de gen.", "de febr.", "de març", "d’abr.", "de maig", "de juny", "de jul.", "d’ag.",
+            "de set.", "d’oct.", "de nov.", "de des.",
+        ],
+        [
+            "GN", "FB", "MÇ", "AB", "MG", "JN", "JL", "AG", "ST", "OC", "NV", "DS",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "diumenge",
+            "dilluns",
+            "dimarts",
+            "dimecres",
+            "dijous",
+            "divendres",
+            "dissabte",
+        ],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("aC", "dC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ca-FR` locale.
+pub static CA_FR: Locale<'static> = Locale {
+    lang: "ca-FR",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM 'del' y",
+        "d MMMM 'del' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de gener",
+            "de febrer",
+            "de març",
+            "d’abril",
+            "de maig",
+            "de juny",
+            "de juliol",
+            "d’agost",
+            "de setembre",
+            "d’octubre",
+            "de novembre",
+            "de desembre",
+        ],
+        [
+            "de gen.", "de febr.", "de març", "d’abr.", "de maig", "de juny", "de jul.", "d’ag.",
+            "de set.", "d’oct.", "de nov.", "de des.",
+        ],
+        [
+            "GN", "FB", "MÇ", "AB", "MG", "JN", "JL", "AG", "ST", "OC", "NV", "DS",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "diumenge",
+            "dilluns",
+            "dimarts",
+            "dimecres",
+            "dijous",
+            "divendres",
+            "dissabte",
+        ],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("aC", "dC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ca-IT` locale.
+pub static CA_IT: Locale<'static> = Locale {
+    lang: "ca-IT",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM 'del' y",
+        "d MMMM 'del' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de gener",
+            "de febrer",
+            "de març",
+            "d’abril",
+            "de maig",
+            "de juny",
+            "de juliol",
+            "d’agost",
+            "de setembre",
+            "d’octubre",
+            "de novembre",
+            "de desembre",
+        ],
+        [
+            "de gen.", "de febr.", "de març", "d’abr.", "de maig", "de juny", "de jul.", "d’ag.",
+            "de set.", "d’oct.", "de nov.", "de des.",
+        ],
+        [
+            "GN", "FB", "MÇ", "AB", "MG", "JN", "JL", "AG", "ST", "OC", "NV", "DS",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "diumenge",
+            "dilluns",
+            "dimarts",
+            "dimecres",
+            "dijous",
+            "divendres",
+            "dissabte",
+        ],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+        ["dg.", "dl.", "dt.", "dc.", "dj.", "dv.", "ds."],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("aC", "dC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `cad` locale.
+pub static CAD: Locale<'static> = Locale {
+    lang: "cad",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Cháykáhday Haˀimay",
+            "Tsahkápbiˀ",
+            "Wánit",
+            "Háshnihtiˀtiˀ",
+            "Háshnih Haˀimay",
+            "Háshnihtsiˀ",
+            "Násˀahˀatsus",
+            "Dahósikah nish",
+            "Híisikah nish",
+            "Nípbaatiˀtiˀ",
+            "Nípbaa Haˀimay",
+            "Cháykáhdaytiˀtiˀ",
+        ],
+        [
+            "Cháykáhday Haˀimay",
+            "Tsahkápbiˀ",
+            "Wánit",
+            "Háshnihtiˀtiˀ",
+            "Háshnih Haˀimay",
+            "Háshnihtsiˀ",
+            "Násˀahˀatsus",
+            "Dahósikah nish",
+            "Híisikah nish",
+            "Nípbaatiˀtiˀ",
+            "Nípbaa Haˀimay",
+            "Cháykáhdaytiˀtiˀ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Inikuˀ",
+            "Wísts’i hayashuh",
+            "Bít hayashuh",
+            "Dahó hayashuh",
+            "Hiwí hayashuh",
+            "Dissik’an hayashuh",
+            "Inikuˀtiˀtiˀ",
+        ],
+        [
+            "Inikuˀ",
+            "Wísts’i hayashuh",
+            "Bít hayashuh",
+            "Dahó hayashuh",
+            "Hiwí hayashuh",
+            "Dissik’an hayashuh",
+            "Inikuˀtiˀtiˀ",
+        ],
+        [
+            "Inikuˀ",
+            "Wísts’i hayashuh",
+            "Bít hayashuh",
+            "Dahó hayashuh",
+            "Hiwí hayashuh",
+            "Dissik’an hayashuh",
+            "Inikuˀtiˀtiˀ",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cch` locale.
+pub static CCH: Locale<'static> = Locale {
+    lang: "cch",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ccp` locale.
+pub static CCP: Locale<'static> = Locale {
+    lang: "ccp",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𑄎𑄚𑄪𑄠𑄢𑄨",
+            "𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨",
+            "𑄟𑄢𑄴𑄌𑄧",
+            "𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪𑄣𑄭",
+            "𑄃𑄉𑄧𑄌𑄴𑄑𑄴",
+            "𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄃𑄧𑄇𑄴𑄑𑄬𑄝𑄧𑄢𑄴",
+            "𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+        ],
+        [
+            "𑄎𑄚𑄪",
+            "𑄜𑄬𑄛𑄴",
+            "𑄟𑄢𑄴𑄌𑄧",
+            "𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪𑄣𑄭",
+            "𑄃𑄉𑄧𑄌𑄴𑄑𑄴",
+            "𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴",
+            "𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄢𑄴",
+        ],
+        [
+            "𑄎",
+            "𑄜𑄬",
+            "𑄟",
+            "𑄃𑄬",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪",
+            "𑄃",
+            "𑄥𑄬",
+            "𑄃𑄧",
+            "𑄚𑄧",
+            "𑄓𑄨",
+        ],
+    ),
+    days: Locale::new_days(
+        ["𑄢𑄧𑄝𑄨𑄝𑄢𑄴", "𑄥𑄧𑄟𑄴𑄝𑄢𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴𑄝𑄢𑄴", "𑄝𑄪𑄖𑄴𑄝𑄢𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴𑄝𑄢𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴𑄝𑄢𑄴", "𑄥𑄧𑄚𑄨𑄝𑄢𑄴"],
+        ["𑄢𑄧𑄝𑄨", "𑄥𑄧𑄟𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴", "𑄝𑄪𑄖𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴", "𑄥𑄧𑄚𑄨"],
+        ["𑄢𑄧𑄝𑄨", "𑄥𑄧𑄟𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴", "𑄝𑄪𑄖𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴", "𑄥𑄧𑄚𑄨"],
+        ["𑄢𑄧", "𑄥𑄧", "𑄟𑄧", "𑄝𑄪", "𑄝𑄳𑄢𑄨", "𑄥𑄪", "𑄥𑄧"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("𑄈𑄳𑄢𑄨𑄌𑄴𑄑𑄴𑄛𑄫𑄢𑄴𑄝𑄧", "𑄈𑄳𑄢𑄨𑄌𑄴𑄑𑄛𑄴𑄘𑄧"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ccp-IN` locale.
+pub static CCP_IN: Locale<'static> = Locale {
+    lang: "ccp-IN",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𑄎𑄚𑄪𑄠𑄢𑄨",
+            "𑄜𑄬𑄛𑄴𑄝𑄳𑄢𑄪𑄠𑄢𑄨",
+            "𑄟𑄢𑄴𑄌𑄧",
+            "𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪𑄣𑄭",
+            "𑄃𑄉𑄧𑄌𑄴𑄑𑄴",
+            "𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄃𑄧𑄇𑄴𑄑𑄬𑄝𑄧𑄢𑄴",
+            "𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+        ],
+        [
+            "𑄎𑄚𑄪",
+            "𑄜𑄬𑄛𑄴",
+            "𑄟𑄢𑄴𑄌𑄧",
+            "𑄃𑄬𑄛𑄳𑄢𑄨𑄣𑄴",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪𑄣𑄭",
+            "𑄃𑄉𑄧𑄌𑄴𑄑𑄴",
+            "𑄥𑄬𑄛𑄴𑄑𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄃𑄧𑄇𑄴𑄑𑄮𑄝𑄧𑄢𑄴",
+            "𑄚𑄧𑄞𑄬𑄟𑄴𑄝𑄧𑄢𑄴",
+            "𑄓𑄨𑄥𑄬𑄟𑄴𑄝𑄢𑄴",
+        ],
+        [
+            "𑄎",
+            "𑄜𑄬",
+            "𑄟",
+            "𑄃𑄬",
+            "𑄟𑄬",
+            "𑄎𑄪𑄚𑄴",
+            "𑄎𑄪",
+            "𑄃",
+            "𑄥𑄬",
+            "𑄃𑄧",
+            "𑄚𑄧",
+            "𑄓𑄨",
+        ],
+    ),
+    days: Locale::new_days(
+        ["𑄢𑄧𑄝𑄨𑄝𑄢𑄴", "𑄥𑄧𑄟𑄴𑄝𑄢𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴𑄝𑄢𑄴", "𑄝𑄪𑄖𑄴𑄝𑄢𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴𑄝𑄢𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴𑄝𑄢𑄴", "𑄥𑄧𑄚𑄨𑄝𑄢𑄴"],
+        ["𑄢𑄧𑄝𑄨", "𑄥𑄧𑄟𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴", "𑄝𑄪𑄖𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴", "𑄥𑄧𑄚𑄨"],
+        ["𑄢𑄧𑄝𑄨", "𑄥𑄧𑄟𑄴", "𑄟𑄧𑄁𑄉𑄧𑄣𑄴", "𑄝𑄪𑄖𑄴", "𑄝𑄳𑄢𑄨𑄥𑄪𑄛𑄴", "𑄥𑄪𑄇𑄴𑄇𑄮𑄢𑄴", "𑄥𑄧𑄚𑄨"],
+        ["𑄢𑄧", "𑄥𑄧", "𑄟𑄧", "𑄝𑄪", "𑄝𑄳𑄢𑄨", "𑄥𑄪", "𑄥𑄧"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("𑄈𑄳𑄢𑄨𑄌𑄴𑄑𑄴𑄛𑄫𑄢𑄴𑄝𑄧", "𑄈𑄳𑄢𑄨𑄌𑄴𑄑𑄛𑄴𑄘𑄧"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ce` locale.
+pub static CE: Locale<'static> = Locale {
+    lang: "ce",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "январь",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "кӀира",
+            "оршот",
+            "шинара",
+            "кхаара",
+            "еара",
+            "пӀераска",
+            "шуот",
+        ],
+        ["кӀи", "ор", "ши", "кха", "еа", "пӀе", "шуо"],
+        ["кӀи", "ор", "ши", "кха", "еа", "пӀе", "шуо"],
+        ["кӀи", "ор", "ши", "кха", "еа", "пӀе", "шуо"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("в. э. тӀ. я", "в. э"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ceb` locale.
+pub static CEB: Locale<'static> = Locale {
+    lang: "ceb",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Enero",
+            "Pebrero",
+            "Marso",
+            "Abril",
+            "Mayo",
+            "Hunyo",
+            "Hulyo",
+            "Agosto",
+            "Septiyembre",
+            "Oktubre",
+            "Nobyembre",
+            "Disyembre",
+        ],
+        [
+            "Ene", "Peb", "Mar", "Abr", "May", "Hun", "Hul", "Ago", "Sep", "Okt", "Nob", "Dis",
+        ],
+        ["E", "P", "M", "A", "M", "H", "H", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Domingo",
+            "Lunes",
+            "Martes",
+            "Miyerkules",
+            "Huwebes",
+            "Biyernes",
+            "Sabado",
+        ],
+        ["Dom", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab"],
+        ["Dom", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab"],
+        ["D", "L", "M", "M", "H", "B", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cgg` locale.
+pub static CGG: Locale<'static> = Locale {
+    lang: "cgg",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Okwokubanza",
+            "Okwakabiri",
+            "Okwakashatu",
+            "Okwakana",
+            "Okwakataana",
+            "Okwamukaaga",
+            "Okwamushanju",
+            "Okwamunaana",
+            "Okwamwenda",
+            "Okwaikumi",
+            "Okwaikumi na kumwe",
+            "Okwaikumi na ibiri",
+        ],
+        [
+            "KBZ", "KBR", "KST", "KKN", "KTN", "KMK", "KMS", "KMN", "KMW", "KKM", "KNK", "KNB",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sande",
+            "Orwokubanza",
+            "Orwakabiri",
+            "Orwakashatu",
+            "Orwakana",
+            "Orwakataano",
+            "Orwamukaaga",
+        ],
+        ["SAN", "ORK", "OKB", "OKS", "OKN", "OKT", "OMK"],
+        ["SAN", "ORK", "OKB", "OKS", "OKN", "OKT", "OMK"],
+        ["S", "K", "R", "S", "N", "T", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cho` locale.
+pub static CHO: Locale<'static> = Locale {
+    lang: "cho",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `chr` locale.
+pub static CHR: Locale<'static> = Locale {
+    lang: "chr",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ᎤᏃᎸᏔᏅ",
+            "ᎧᎦᎵ",
+            "ᎠᏅᏱ",
+            "ᎧᏬᏂ",
+            "ᎠᏂᏍᎬᏘ",
+            "ᏕᎭᎷᏱ",
+            "ᎫᏰᏉᏂ",
+            "ᎦᎶᏂ",
+            "ᏚᎵᏍᏗ",
+            "ᏚᏂᏅᏗ",
+            "ᏅᏓᏕᏆ",
+            "ᎥᏍᎩᏱ",
+        ],
+        [
+            "ᎤᏃ", "ᎧᎦ", "ᎠᏅ", "ᎧᏬ", "ᎠᏂ", "ᏕᎭ", "ᎫᏰ", "ᎦᎶ", "ᏚᎵ", "ᏚᏂ", "ᏅᏓ", "ᎥᏍ",
+        ],
+        ["Ꭴ", "Ꭷ", "Ꭰ", "Ꭷ", "Ꭰ", "Ꮥ", "Ꭻ", "Ꭶ", "Ꮪ", "Ꮪ", "Ꮕ", "Ꭵ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ᎤᎾᏙᏓᏆᏍᎬ",
+            "ᎤᎾᏙᏓᏉᏅᎯ",
+            "ᏔᎵᏁᎢᎦ",
+            "ᏦᎢᏁᎢᎦ",
+            "ᏅᎩᏁᎢᎦ",
+            "ᏧᎾᎩᎶᏍᏗ",
+            "ᎤᎾᏙᏓᏈᏕᎾ",
+        ],
+        ["ᏆᏍᎬ", "ᏉᏅᎯ", "ᏔᎵᏁ", "ᏦᎢᏁ", "ᏅᎩᏁ", "ᏧᎾᎩ", "ᏈᏕᎾ"],
+        ["ᏍᎬ", "ᏅᎯ", "ᏔᎵ", "ᏦᎢ", "ᏅᎩ", "ᏧᎾ", "ᏕᎾ"],
+        ["Ꮖ", "Ꮙ", "Ꮤ", "Ꮶ", "Ꮕ", "Ꮷ", "Ꭴ"],
+    ),
+    day_periods: Locale::new_day_periods(["ᏌᎾᎴ", "ᏒᎯᏱᎢᏗᏢ"], ["ᏌᎾᎴ", "ᏒᎯᏱᎢ"], ["Ꮜ", "Ꮢ"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cic` locale.
+pub static CIC: Locale<'static> = Locale {
+    lang: "cic",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Hashiʼ Ammoʼnaʼ",
+            "Hashiʼ Atokloʼ",
+            "Hashiʼ Atochchíʼnaʼ",
+            "Iiplal",
+            "Mih",
+            "Choon",
+            "Choola",
+            "Akaas",
+            "Siptimpaʼ",
+            "Aaktopaʼ",
+            "Nofimpaʼ",
+            "Tiisimpaʼ",
+        ],
+        [
+            "Hashiʼ Ammoʼnaʼ",
+            "Hashiʼ Atokloʼ",
+            "Hashiʼ Atochchíʼnaʼ",
+            "Iiplal",
+            "Mih",
+            "Choon",
+            "Choola",
+            "Akaas",
+            "Siptimpaʼ",
+            "Aaktopaʼ",
+            "Nofimpaʼ",
+            "Tiisimpaʼ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Nittak Holloʼ",
+            "Mantiʼ",
+            "Chostiʼ",
+            "Winstiʼ",
+            "Soistiʼ",
+            "Nannalhchifaʼ Nittak",
+            "Nittak Holloʼ Nakfish",
+        ],
+        [
+            "Nittak Holloʼ",
+            "Mantiʼ",
+            "Chostiʼ",
+            "Winstiʼ",
+            "Soistiʼ",
+            "Nannalhchifaʼ Nittak",
+            "Nittak Holloʼ Nakfish",
+        ],
+        [
+            "Nittak Holloʼ",
+            "Mantiʼ",
+            "Chostiʼ",
+            "Winstiʼ",
+            "Soistiʼ",
+            "Nannalhchifaʼ Nittak",
+            "Nittak Holloʼ Nakfish",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ckb` locale.
+pub static CKB: Locale<'static> = Locale {
+    lang: "ckb",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "dی MMMMی y", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "کانوونی دووەم",
+            "شوبات",
+            "ئازار",
+            "نیسان",
+            "ئایار",
+            "حوزەیران",
+            "تەمووز",
+            "ئاب",
+            "ئەیلوول",
+            "تشرینی یەکەم",
+            "تشرینی دووەم",
+            "کانونی یەکەم",
+        ],
+        [
+            "کانوونی دووەم",
+            "شوبات",
+            "ئازار",
+            "نیسان",
+            "ئایار",
+            "حوزەیران",
+            "تەمووز",
+            "ئاب",
+            "ئەیلوول",
+            "تشرینی یەکەم",
+            "تشرینی دووەم",
+            "کانونی یەکەم",
+        ],
+        ["ک", "ش", "ئ", "ن", "ئ", "ح", "ت", "ئ", "ئ", "ت", "ت", "ک"],
+    ),
+    days: Locale::new_days(
+        [
+            "یەکشەممە",
+            "دووشەممە",
+            "سێشەممە",
+            "چوارشەممە",
+            "پێنجشەممە",
+            "ھەینی",
+            "شەممە",
+        ],
+        [
+            "یەکشەممە",
+            "دووشەممە",
+            "سێشەممە",
+            "چوارشەممە",
+            "پێنجشەممە",
+            "ھەینی",
+            "شەممە",
+        ],
+        ["١ش", "٢ش", "٣ش", "٤ش", "٥ش", "ھ", "ش"],
+        ["ی", "د", "س", "چ", "پ", "ھ", "ش"],
+    ),
+    day_periods: Locale::new_day_periods(["ب.ن", "د.ن"], ["ب.ن", "د.ن"], ["ب.ن", "د.ن"]),
+    eras: Locale::new_eras("پێش زایین", "زایینی"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ckb-IR` locale.
+pub static CKB_IR: Locale<'static> = Locale {
+    lang: "ckb-IR",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "dی MMMMی y", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "کانوونی دووەم",
+            "شوبات",
+            "ئازار",
+            "نیسان",
+            "ئایار",
+            "حوزەیران",
+            "تەمووز",
+            "ئاب",
+            "ئەیلوول",
+            "تشرینی یەکەم",
+            "تشرینی دووەم",
+            "کانونی یەکەم",
+        ],
+        [
+            "کانوونی دووەم",
+            "شوبات",
+            "ئازار",
+            "نیسان",
+            "ئایار",
+            "حوزەیران",
+            "تەمووز",
+            "ئاب",
+            "ئەیلوول",
+            "تشرینی یەکەم",
+            "تشرینی دووەم",
+            "کانونی یەکەم",
+        ],
+        ["ک", "ش", "ئ", "ن", "ئ", "ح", "ت", "ئ", "ئ", "ت", "ت", "ک"],
+    ),
+    days: Locale::new_days(
+        [
+            "یەکشەممە",
+            "دووشەممە",
+            "سێشەممە",
+            "چوارشەممە",
+            "پێنجشەممە",
+            "ھەینی",
+            "شەممە",
+        ],
+        [
+            "یەکشەممە",
+            "دووشەممە",
+            "سێشەممە",
+            "چوارشەممە",
+            "پێنجشەممە",
+            "ھەینی",
+            "شەممە",
+        ],
+        ["١ش", "٢ش", "٣ش", "٤ش", "٥ش", "ھ", "ش"],
+        ["ی", "د", "س", "چ", "پ", "ھ", "ش"],
+    ),
+    day_periods: Locale::new_day_periods(["ب.ن", "د.ن"], ["ب.ن", "د.ن"], ["ب.ن", "د.ن"]),
+    eras: Locale::new_eras("پێش زایین", "زایینی"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `co` locale.
+pub static CO: Locale<'static> = Locale {
+    lang: "co",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cop` locale.
+pub static COP: Locale<'static> = Locale {
+    lang: "cop",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cs` locale.
+pub static CS: Locale<'static> = Locale {
+    lang: "cs",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. M. y", "dd.MM.yy"),
+    times: Locale::new_patterns("H:mm:ss, zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ledna",
+            "února",
+            "března",
+            "dubna",
+            "května",
+            "června",
+            "července",
+            "srpna",
+            "září",
+            "října",
+            "listopadu",
+            "prosince",
+        ],
+        [
+            "led", "úno", "bře", "dub", "kvě", "čvn", "čvc", "srp", "zář", "říj", "lis", "pro",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "neděle",
+            "pondělí",
+            "úterý",
+            "středa",
+            "čtvrtek",
+            "pátek",
+            "sobota",
+        ],
+        ["ne", "po", "út", "st", "čt", "pá", "so"],
+        ["ne", "po", "út", "st", "čt", "pá", "so"],
+        ["N", "P", "Ú", "S", "Č", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["dop.", "odp."], ["dop.", "odp."], ["dop.", "odp."]),
+    eras: Locale::new_eras("př. n. l.", "n. l."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `csw` locale.
+pub static CSW: Locale<'static> = Locale {
+    lang: "csw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ᐅᒉᒥᑮᓯᑳᐏᐲᓯᒼ",
+            "ᐸᐚᐦᒐᑭᓇᓰᐢ",
+            "ᒥᑭᓯᐏᐲᓯᒼ",
+            "ᓂᐢᑭᐲᓯᒼ",
+            "ᐊᓃᑭᐲᓯᒼ",
+            "ᐚᐏᐲᓯᒼ",
+            "ᐹᐢᑲᐦᐋᐏᐲᓯᒼ",
+            "ᐅᐸᐦᐅᐏᐲᓯᒼ",
+            "ᓄᒌᑐᐏᐲᓯᒼ",
+            "ᐱᓈᐢᑯᐏᐲᓯᒼ",
+            "ᐋᕽᐘᑎᓄᐏᐲᓯᒼ",
+            "ᒪᑯᓭᑮᓭᑳᐏᐲᓯᒼ",
+        ],
+        [
+            "ᐅᒉᒥᑮᓯᑳᐏᐲᓯᒼ",
+            "ᐸᐚᐦᒐᑭᓇᓰᐢ",
+            "ᒥᑭᓯᐏᐲᓯᒼ",
+            "ᓂᐢᑭᐲᓯᒼ",
+            "ᐊᓃᑭᐲᓯᒼ",
+            "ᐚᐏᐲᓯᒼ",
+            "ᐹᐢᑲᐦᐋᐏᐲᓯᒼ",
+            "ᐅᐸᐦᐅᐏᐲᓯᒼ",
+            "ᓄᒌᑐᐏᐲᓯᒼ",
+            "ᐱᓈᐢᑯᐏᐲᓯᒼ",
+            "ᐋᕽᐘᑎᓄᐏᐲᓯᒼ",
+            "ᒪᑯᓭᑮᓭᑳᐏᐲᓯᒼ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᐴᓂ ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᓃᓱᑮᓯᑳᐤ",
+            "ᐋᐱᐦᑕᐘᐣ",
+            "ᐴᓂᐋᐱᐦᑕᐘᐣ",
+            "ᑫᑳᐨ ᒫᑎᓇᐍᑮᓯᑳᐤ",
+            "ᒫᑎᓇᐍᑮᓯᑳᐤ",
+        ],
+        [
+            "ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᐴᓂ ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᓃᓱᑮᓯᑳᐤ",
+            "ᐋᐱᐦᑕᐘᐣ",
+            "ᐴᓂᐋᐱᐦᑕᐘᐣ",
+            "ᑫᑳᐨ ᒫᑎᓇᐍᑮᓯᑳᐤ",
+            "ᒫᑎᓇᐍᑮᓯᑳᐤ",
+        ],
+        [
+            "ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᐴᓂ ᐊᔭᒥᐦᐁᑮᓯᑳᐤ",
+            "ᓃᓱᑮᓯᑳᐤ",
+            "ᐋᐱᐦᑕᐘᐣ",
+            "ᐴᓂᐋᐱᐦᑕᐘᐣ",
+            "ᑫᑳᐨ ᒫᑎᓇᐍᑮᓯᑳᐤ",
+            "ᒫᑎᓇᐍᑮᓯᑳᐤ",
+        ],
+        ["ᐊ", "ᐴ", "ᓃ", "ᐋ", "ᐴ", "ᑫ", "ᒫ"],
+    ),
+    day_periods: Locale::new_day_periods(["ᐁᒼ", "ᐲᒼ"], ["ᐊᒼ", "ᐲᒼ"], ["ᐊᒼ", "ᐲᒼ"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cu` locale.
+pub static CU: Locale<'static> = Locale {
+    lang: "cu",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `cv` locale.
+pub static CV: Locale<'static> = Locale {
+    lang: "cv",
+    dates: Locale::new_patterns("y, MMMM, d, EEEE", "y, MMMM, d", "y, MMM, d", "y.MM.dd"),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "кӑрлач",
+            "нарӑс",
+            "пуш",
+            "ака",
+            "ҫу",
+            "ҫӗртме",
+            "утӑ",
+            "ҫурла",
+            "авӑн",
+            "юпа",
+            "чӳк",
+            "раштав",
+        ],
+        [
+            "кӑр.",
+            "нар.",
+            "пуш",
+            "ака",
+            "ҫу",
+            "ҫӗрт.",
+            "утӑ",
+            "ҫур.",
+            "авӑн",
+            "юпа",
+            "чӳк",
+            "раш.",
+        ],
+        ["К", "Н", "П", "А", "Ҫ", "Ҫ", "У", "Ҫ", "А", "Ю", "Ч", "Р"],
+    ),
+    days: Locale::new_days(
+        [
+            "вырсарни кун",
+            "тунти кун",
+            "ытлари кун",
+            "юн кун",
+            "кӗҫнерни кун",
+            "эрне кун",
+            "шӑмат кун",
+        ],
+        ["вр", "тн", "ыт", "юн", "кҫ", "эр", "шм"],
+        ["вр", "тн", "ыт", "юн", "кҫ", "эр", "шм"],
+        ["В", "Т", "Ы", "Ю", "К", "Э", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["к. у.", "к. х."],
+        ["к. у.", "к. х."],
+        ["к. у.", "к. х."],
+    ),
+    eras: Locale::new_eras("х. с-ч.", "х. с."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `cy` locale.
+pub static CY: Locale<'static> = Locale {
+    lang: "cy",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ionawr",
+            "Chwefror",
+            "Mawrth",
+            "Ebrill",
+            "Mai",
+            "Mehefin",
+            "Gorffennaf",
+            "Awst",
+            "Medi",
+            "Hydref",
+            "Tachwedd",
+            "Rhagfyr",
+        ],
+        [
+            "Ion", "Chwef", "Maw", "Ebr", "Mai", "Meh", "Gorff", "Awst", "Medi", "Hyd", "Tach",
+            "Rhag",
+        ],
+        ["I", "Ch", "M", "E", "M", "M", "G", "A", "M", "H", "T", "Rh"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dydd Sul",
+            "Dydd Llun",
+            "Dydd Mawrth",
+            "Dydd Mercher",
+            "Dydd Iau",
+            "Dydd Gwener",
+            "Dydd Sadwrn",
+        ],
+        ["Sul", "Llun", "Maw", "Mer", "Iau", "Gwen", "Sad"],
+        ["Su", "Ll", "Ma", "Me", "Ia", "Gw", "Sa"],
+        ["S", "Ll", "M", "M", "I", "G", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["yb", "yh"], ["AM", "PM"], ["b", "h"]),
+    eras: Locale::new_eras("CC", "OC"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `da` locale.
+pub static DA: Locale<'static> = Locale {
+    lang: "da",
+    dates: Locale::new_patterns("EEEE 'den' d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "marts",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "maj", "jun.", "jul.", "aug.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag",
+        ],
+        ["søn.", "man.", "tirs.", "ons.", "tors.", "fre.", "lør."],
+        ["sø.", "ma.", "ti.", "on.", "to.", "fr.", "lø."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `da-GL` locale.
+pub static DA_GL: Locale<'static> = Locale {
+    lang: "da-GL",
+    dates: Locale::new_patterns("EEEE 'den' d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "marts",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "maj", "jun.", "jul.", "aug.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag",
+        ],
+        ["søn.", "man.", "tirs.", "ons.", "tors.", "fre.", "lør."],
+        ["sø.", "ma.", "ti.", "on.", "to.", "fr.", "lø."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `dav` locale.
+pub static DAV: Locale<'static> = Locale {
+    lang: "dav",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mori ghwa imbiri",
+            "Mori ghwa kawi",
+            "Mori ghwa kadadu",
+            "Mori ghwa kana",
+            "Mori ghwa kasanu",
+            "Mori ghwa karandadu",
+            "Mori ghwa mfungade",
+            "Mori ghwa wunyanya",
+            "Mori ghwa ikenda",
+            "Mori ghwa ikumi",
+            "Mori ghwa ikumi na imweri",
+            "Mori ghwa ikumi na iwi",
+        ],
+        [
+            "Imb", "Kaw", "Kad", "Kan", "Kas", "Kar", "Mfu", "Wun", "Ike", "Iku", "Imw", "Iwi",
+        ],
+        ["I", "K", "K", "K", "K", "K", "M", "W", "I", "I", "I", "I"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ituku ja jumwa",
+            "Kuramuka jimweri",
+            "Kuramuka kawi",
+            "Kuramuka kadadu",
+            "Kuramuka kana",
+            "Kuramuka kasanu",
+            "Kifula nguwo",
+        ],
+        ["Jum", "Jim", "Kaw", "Kad", "Kan", "Kas", "Ngu"],
+        ["Jum", "Jim", "Kaw", "Kad", "Kan", "Kas", "Ngu"],
+        ["J", "J", "K", "K", "K", "K", "N"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Luma lwa K", "luma lwa p"],
+        ["Luma lwa K", "luma lwa p"],
+        ["Luma lwa K", "luma lwa p"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `de` locale.
+pub static DE: Locale<'static> = Locale {
+    lang: "de",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `de-AT` locale.
+pub static DE_AT: Locale<'static> = Locale {
+    lang: "de-AT",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Jänner",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jän.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sep.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `de-BE` locale.
+pub static DE_BE: Locale<'static> = Locale {
+    lang: "de-BE",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `de-CH` locale.
+pub static DE_CH: Locale<'static> = Locale {
+    lang: "de-CH",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `de-IT` locale.
+pub static DE_IT: Locale<'static> = Locale {
+    lang: "de-IT",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Jänner",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jän.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sep.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `de-LI` locale.
+pub static DE_LI: Locale<'static> = Locale {
+    lang: "de-LI",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `de-LU` locale.
+pub static DE_LU: Locale<'static> = Locale {
+    lang: "de-LU",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonntag",
+            "Montag",
+            "Dienstag",
+            "Mittwoch",
+            "Donnerstag",
+            "Freitag",
+            "Samstag",
+        ],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["vorm.", "nachm."]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `dje` locale.
+pub static DJE: Locale<'static> = Locale {
+    lang: "dje",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Žanwiye",
+            "Feewiriye",
+            "Marsi",
+            "Awiril",
+            "Me",
+            "Žuweŋ",
+            "Žuyye",
+            "Ut",
+            "Sektanbur",
+            "Oktoobur",
+            "Noowanbur",
+            "Deesanbur",
+        ],
+        [
+            "Žan", "Fee", "Mar", "Awi", "Me", "Žuw", "Žuy", "Ut", "Sek", "Okt", "Noo", "Dee",
+        ],
+        ["Ž", "F", "M", "A", "M", "Ž", "Ž", "U", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Alhadi", "Atinni", "Atalaata", "Alarba", "Alhamisi", "Alzuma", "Asibti",
+        ],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["H", "T", "T", "L", "M", "Z", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Subbaahi", "Zaarikay b"],
+        ["Subbaahi", "Zaarikay b"],
+        ["Subbaahi", "Zaarikay b"],
+    ),
+    eras: Locale::new_eras("IJ", "IZ"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `doi` locale.
+pub static DOI: Locale<'static> = Locale {
+    lang: "doi",
+    dates: Locale::new_patterns("EEEE, d, MMMM y", "d, MMMM y", "d, MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मेई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितंबर",
+            "अक्तूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जन.",
+            "फर.",
+            "मार्च",
+            "अप्रैल",
+            "मेई",
+            "जून",
+            "जुलाई",
+            "अग.",
+            "सित.",
+            "अक्तू.",
+            "नव.",
+            "दिस.",
+        ],
+        [
+            "ज", "फ", "मा", "अ", "मे", "जू", "जु", "अ", "सि", "अ", "न", "दि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ऐतबार",
+            "सोमबार",
+            "मंगलबार",
+            "बुधबार",
+            "बीरबार",
+            "शुक्रबार",
+            "शनिबार",
+        ],
+        ["ऐत", "सोम", "मंगल", "बुध", "बीर", "शुक्र", "शनि"],
+        ["ऐत", "सोम", "मंगल", "बुध", "बीर", "शुक्र", "शनि"],
+        ["ऐ.", "सो.", "म.", "बु.", "बी.", "शु.", "श."],
+    ),
+    day_periods: Locale::new_day_periods(["सवेर", "दपैहर बाद"], ["सवेर", "स’ञ"], ["सवेर", "स’ञ"]),
+    eras: Locale::new_eras("ई.पू.", "ईसवी"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `dsb` locale.
+pub static DSB: Locale<'static> = Locale {
+    lang: "dsb",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d.M.y", "d.M.yy"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januara",
+            "februara",
+            "měrca",
+            "apryla",
+            "maja",
+            "junija",
+            "julija",
+            "awgusta",
+            "septembra",
+            "oktobra",
+            "nowembra",
+            "decembra",
+        ],
+        [
+            "jan.", "feb.", "měr.", "apr.", "maj.", "jun.", "jul.", "awg.", "sep.", "okt.", "now.",
+            "dec.",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "njeźela",
+            "pónjeźele",
+            "wałtora",
+            "srjoda",
+            "stwórtk",
+            "pětk",
+            "sobota",
+        ],
+        ["nje", "pón", "wał", "srj", "stw", "pět", "sob"],
+        ["nj", "pó", "wa", "sr", "st", "pě", "so"],
+        ["n", "p", "w", "s", "s", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["dopołdnja", "wótpołdnja"],
+        ["dop.", "wótp."],
+        ["dop.", "wótp."],
+    ),
+    eras: Locale::new_eras("pś.Chr.n.", "pó Chr.n."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `dua` locale.
+pub static DUA: Locale<'static> = Locale {
+    lang: "dua",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "dimɔ́di",
+            "ŋgɔndɛ",
+            "sɔŋɛ",
+            "diɓáɓá",
+            "emiasele",
+            "esɔpɛsɔpɛ",
+            "madiɓɛ́díɓɛ́",
+            "diŋgindi",
+            "nyɛtɛki",
+            "mayésɛ́",
+            "tiníní",
+            "eláŋgɛ́",
+        ],
+        [
+            "di", "ŋgɔn", "sɔŋ", "diɓ", "emi", "esɔ", "mad", "diŋ", "nyɛt", "may", "tin", "elá",
+        ],
+        ["d", "ŋ", "s", "d", "e", "e", "m", "d", "n", "m", "t", "e"],
+    ),
+    days: Locale::new_days(
+        [
+            "éti",
+            "mɔ́sú",
+            "kwasú",
+            "mukɔ́sú",
+            "ŋgisú",
+            "ɗónɛsú",
+            "esaɓasú",
+        ],
+        ["ét", "mɔ́s", "kwa", "muk", "ŋgi", "ɗón", "esa"],
+        ["ét", "mɔ́s", "kwa", "muk", "ŋgi", "ɗón", "esa"],
+        ["e", "m", "k", "m", "ŋ", "ɗ", "e"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["idiɓa", "ebyámu"],
+        ["idiɓa", "ebyámu"],
+        ["idiɓa", "ebyámu"],
+    ),
+    eras: Locale::new_eras("ɓ.Ys", "mb.Ys"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `dv` locale.
+pub static DV: Locale<'static> = Locale {
+    lang: "dv",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `dyo` locale.
+pub static DYO: Locale<'static> = Locale {
+    lang: "dyo",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Sanvie",
+            "Fébirie",
+            "Mars",
+            "Aburil",
+            "Mee",
+            "Sueŋ",
+            "Súuyee",
+            "Ut",
+            "Settembar",
+            "Oktobar",
+            "Novembar",
+            "Disambar",
+        ],
+        [
+            "Sa", "Fe", "Ma", "Ab", "Me", "Su", "Sú", "Ut", "Se", "Ok", "No", "De",
+        ],
+        ["S", "F", "M", "A", "M", "S", "S", "U", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dimas", "Teneŋ", "Talata", "Alarbay", "Aramisay", "Arjuma", "Sibiti",
+        ],
+        ["Dim", "Ten", "Tal", "Ala", "Ara", "Arj", "Sib"],
+        ["Dim", "Ten", "Tal", "Ala", "Ara", "Arj", "Sib"],
+        ["D", "T", "T", "A", "A", "A", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ArY", "AtY"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `dz` locale.
+pub static DZ: Locale<'static> = Locale {
+    lang: "dz",
+    dates: Locale::new_patterns(
+        "EEEE, སྤྱི་ལོ་y MMMM ཚེས་dd",
+        "སྤྱི་ལོ་y MMMM ཚེས་ dd",
+        "སྤྱི་ལོ་y ཟླ་MMM ཚེས་dd",
+        "y-MM-dd",
+    ),
+    times: Locale::new_patterns(
+        "ཆུ་ཚོད་ h སྐར་མ་ mm:ss a zzzz",
+        "ཆུ་ཚོད་ h སྐར་མ་ mm:ss a z",
+        "ཆུ་ཚོད་h:mm:ss a",
+        "ཆུ་ཚོད་ h སྐར་མ་ mm a",
+    ),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ཟླ་དངཔ་",
+            "ཟླ་གཉིས་པ་",
+            "ཟླ་གསུམ་པ་",
+            "ཟླ་བཞི་པ་",
+            "ཟླ་ལྔ་པ་",
+            "ཟླ་དྲུག་པ",
+            "ཟླ་བདུན་པ་",
+            "ཟླ་བརྒྱད་པ་",
+            "ཟླ་དགུ་པ་",
+            "ཟླ་བཅུ་པ་",
+            "ཟླ་བཅུ་གཅིག་པ་",
+            "ཟླ་བཅུ་གཉིས་པ་",
+        ],
+        [
+            "༡", "༢", "༣", "༤", "༥", "༦", "༧", "༨", "༩", "༡༠", "༡༡", "12",
+        ],
+        [
+            "༡", "༢", "༣", "4", "༥", "༦", "༧", "༨", "9", "༡༠", "༡༡", "༡༢",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "གཟའ་ཟླ་བ་",
+            "གཟའ་མིག་དམར་",
+            "གཟའ་ལྷག་པ་",
+            "གཟའ་ཕུར་བུ་",
+            "གཟའ་པ་སངས་",
+            "གཟའ་སྤེན་པ་",
+            "གཟའ་ཉི་མ་",
+        ],
+        ["ཟླ་", "མིར་", "ལྷག་", "ཕུར་", "སངས་", "སྤེན་", "ཉི་"],
+        ["ཟླ་", "མིར་", "ལྷག་", "ཕུར་", "སངས་", "སྤེན་", "ཉི་"],
+        ["ཟླ", "མིར", "ལྷག", "ཕུར", "སངྶ", "སྤེན", "ཉི"],
+    ),
+    day_periods: Locale::new_day_periods(["སྔ་ཆ་", "ཕྱི་ཆ་"], ["སྔ་ཆ་", "ཕྱི་ཆ་"], ["སྔ་ཆ་", "ཕྱི་ཆ་"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ebu` locale.
+pub static EBU: Locale<'static> = Locale {
+    lang: "ebu",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mweri wa mbere",
+            "Mweri wa kaĩri",
+            "Mweri wa kathatũ",
+            "Mweri wa kana",
+            "Mweri wa gatano",
+            "Mweri wa gatantatũ",
+            "Mweri wa mũgwanja",
+            "Mweri wa kanana",
+            "Mweri wa kenda",
+            "Mweri wa ikũmi",
+            "Mweri wa ikũmi na ũmwe",
+            "Mweri wa ikũmi na Kaĩrĩ",
+        ],
+        [
+            "Mbe", "Kai", "Kat", "Kan", "Gat", "Gan", "Mug", "Knn", "Ken", "Iku", "Imw", "Igi",
+        ],
+        ["M", "K", "K", "K", "G", "G", "M", "K", "K", "I", "I", "I"],
+    ),
+    days: Locale::new_days(
+        [
+            "Kiumia",
+            "Njumatatu",
+            "Njumaine",
+            "Njumatano",
+            "Aramithi",
+            "Njumaa",
+            "NJumamothii",
+        ],
+        ["Kma", "Tat", "Ine", "Tan", "Arm", "Maa", "NMM"],
+        ["Kma", "Tat", "Ine", "Tan", "Arm", "Maa", "NMM"],
+        ["K", "N", "N", "N", "A", "M", "N"],
+    ),
+    day_periods: Locale::new_day_periods(["KI", "UT"], ["KI", "UT"], ["KI", "UT"]),
+    eras: Locale::new_eras("MK", "TK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ee` locale.
+pub static EE: Locale<'static> = Locale {
+    lang: "ee",
+    dates: Locale::new_patterns(
+        "EEEE, MMMM d 'lia' y",
+        "MMMM d 'lia' y",
+        "MMM d 'lia', y",
+        "M/d/yy",
+    ),
+    times: Locale::new_patterns(
+        "a 'ga' h:mm:ss zzzz",
+        "a 'ga' h:mm:ss z",
+        "a 'ga' h:mm:ss",
+        "a 'ga' h:mm",
+    ),
+    date_times: Locale::new_patterns("{0} {1}", "{0} {1}", "{0} {1}", "{0} {1}"),
+    months: Locale::new_months(
+        [
+            "dzove",
+            "dzodze",
+            "tedoxe",
+            "afɔfĩe",
+            "dame",
+            "masa",
+            "siamlɔm",
+            "deasiamime",
+            "anyɔnyɔ",
+            "kele",
+            "adeɛmekpɔxe",
+            "dzome",
+        ],
+        [
+            "dzv", "dzd", "ted", "afɔ", "dam", "mas", "sia", "dea", "any", "kel", "ade", "dzm",
+        ],
+        ["d", "d", "t", "a", "d", "m", "s", "d", "a", "k", "a", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "kɔsiɖa", "dzoɖa", "blaɖa", "kuɖa", "yawoɖa", "fiɖa", "memleɖa",
+        ],
+        ["kɔs", "dzo", "bla", "kuɖ", "yaw", "fiɖ", "mem"],
+        ["kɔs", "dzo", "bla", "kuɖ", "yaw", "fiɖ", "mem"],
+        ["k", "d", "b", "k", "y", "f", "m"],
+    ),
+    day_periods: Locale::new_day_periods(["ŋdi", "ɣetrɔ"], ["ŋdi", "ɣetrɔ"], ["ŋ", "ɣ"]),
+    eras: Locale::new_eras("HYV", "Yŋ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ee-TG` locale.
+pub static EE_TG: Locale<'static> = Locale {
+    lang: "ee-TG",
+    dates: Locale::new_patterns(
+        "EEEE, MMMM d 'lia' y",
+        "MMMM d 'lia' y",
+        "MMM d 'lia', y",
+        "M/d/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{0} {1}", "{0} {1}", "{0} {1}", "{0} {1}"),
+    months: Locale::new_months(
+        [
+            "dzove",
+            "dzodze",
+            "tedoxe",
+            "afɔfĩe",
+            "dame",
+            "masa",
+            "siamlɔm",
+            "deasiamime",
+            "anyɔnyɔ",
+            "kele",
+            "adeɛmekpɔxe",
+            "dzome",
+        ],
+        [
+            "dzv", "dzd", "ted", "afɔ", "dam", "mas", "sia", "dea", "any", "kel", "ade", "dzm",
+        ],
+        ["d", "d", "t", "a", "d", "m", "s", "d", "a", "k", "a", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "kɔsiɖa", "dzoɖa", "blaɖa", "kuɖa", "yawoɖa", "fiɖa", "memleɖa",
+        ],
+        ["kɔs", "dzo", "bla", "kuɖ", "yaw", "fiɖ", "mem"],
+        ["kɔs", "dzo", "bla", "kuɖ", "yaw", "fiɖ", "mem"],
+        ["k", "d", "b", "k", "y", "f", "m"],
+    ),
+    day_periods: Locale::new_day_periods(["ŋdi", "ɣetrɔ"], ["ŋdi", "ɣetrɔ"], ["ŋ", "ɣ"]),
+    eras: Locale::new_eras("HYV", "Yŋ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `el` locale.
+pub static EL: Locale<'static> = Locale {
+    lang: "el",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} - {0}", "{1} - {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ιανουαρίου",
+            "Φεβρουαρίου",
+            "Μαρτίου",
+            "Απριλίου",
+            "Μαΐου",
+            "Ιουνίου",
+            "Ιουλίου",
+            "Αυγούστου",
+            "Σεπτεμβρίου",
+            "Οκτωβρίου",
+            "Νοεμβρίου",
+            "Δεκεμβρίου",
+        ],
+        [
+            "Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαΐ", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ",
+        ],
+        ["Ι", "Φ", "Μ", "Α", "Μ", "Ι", "Ι", "Α", "Σ", "Ο", "Ν", "Δ"],
+    ),
+    days: Locale::new_days(
+        [
+            "Κυριακή",
+            "Δευτέρα",
+            "Τρίτη",
+            "Τετάρτη",
+            "Πέμπτη",
+            "Παρασκευή",
+            "Σάββατο",
+        ],
+        ["Κυρ", "Δευ", "Τρί", "Τετ", "Πέμ", "Παρ", "Σάβ"],
+        ["Κυ", "Δε", "Τρ", "Τε", "Πέ", "Πα", "Σά"],
+        ["Κ", "Δ", "Τ", "Τ", "Π", "Π", "Σ"],
+    ),
+    day_periods: Locale::new_day_periods(["π.μ.", "μ.μ."], ["π.μ.", "μ.μ."], ["πμ", "μμ"]),
+    eras: Locale::new_eras("π.Χ.", "μ.Χ."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `el-CY` locale.
+pub static EL_CY: Locale<'static> = Locale {
+    lang: "el-CY",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} - {0}", "{1} - {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ιανουαρίου",
+            "Φεβρουαρίου",
+            "Μαρτίου",
+            "Απριλίου",
+            "Μαΐου",
+            "Ιουνίου",
+            "Ιουλίου",
+            "Αυγούστου",
+            "Σεπτεμβρίου",
+            "Οκτωβρίου",
+            "Νοεμβρίου",
+            "Δεκεμβρίου",
+        ],
+        [
+            "Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαΐ", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ",
+        ],
+        ["Ι", "Φ", "Μ", "Α", "Μ", "Ι", "Ι", "Α", "Σ", "Ο", "Ν", "Δ"],
+    ),
+    days: Locale::new_days(
+        [
+            "Κυριακή",
+            "Δευτέρα",
+            "Τρίτη",
+            "Τετάρτη",
+            "Πέμπτη",
+            "Παρασκευή",
+            "Σάββατο",
+        ],
+        ["Κυρ", "Δευ", "Τρί", "Τετ", "Πέμ", "Παρ", "Σάβ"],
+        ["Κυ", "Δε", "Τρ", "Τε", "Πέ", "Πα", "Σά"],
+        ["Κ", "Δ", "Τ", "Τ", "Π", "Π", "Σ"],
+    ),
+    day_periods: Locale::new_day_periods(["π.μ.", "μ.μ."], ["π.μ.", "μ.μ."], ["πμ", "μμ"]),
+    eras: Locale::new_eras("π.Χ.", "μ.Χ."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `el-polyton` locale.
+pub static EL_POLYTON: Locale<'static> = Locale {
+    lang: "el-polyton",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} - {0}", "{1} - {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ιανουαρίου",
+            "Φεβρουαρίου",
+            "Μαρτίου",
+            "Απριλίου",
+            "Μαΐου",
+            "Ιουνίου",
+            "Ιουλίου",
+            "Αὐγούστου",
+            "Σεπτεμβρίου",
+            "Ὀκτωβρίου",
+            "Νοεμβρίου",
+            "Δεκεμβρίου",
+        ],
+        [
+            "Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαΐ", "Ιουν", "Ιουλ", "Αὐγ", "Σεπ", "Ὀκτ", "Νοε", "Δεκ",
+        ],
+        ["Ι", "Φ", "Μ", "Α", "Μ", "Ι", "Ι", "Α", "Σ", "Ο", "Ν", "Δ"],
+    ),
+    days: Locale::new_days(
+        [
+            "Κυριακή",
+            "Δευτέρα",
+            "Τρίτη",
+            "Τετάρτη",
+            "Πέμπτη",
+            "Παρασκευή",
+            "Σάββατο",
+        ],
+        ["Κυρ", "Δευ", "Τρί", "Τετ", "Πέμ", "Παρ", "Σάβ"],
+        ["Κυ", "Δε", "Τρ", "Τε", "Πέ", "Πα", "Σά"],
+        ["Κ", "Δ", "Τ", "Τ", "Π", "Π", "Σ"],
+    ),
+    day_periods: Locale::new_day_periods(["π.μ.", "μ.μ."], ["π.μ.", "μ.μ."], ["πμ", "μμ"]),
+    eras: Locale::new_eras("π.Χ.", "μ.Χ."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en` locale.
+pub static EN: Locale<'static> = Locale {
+    lang: "en",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-001` locale.
+pub static EN_001: Locale<'static> = Locale {
+    lang: "en-001",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-150` locale.
+pub static EN_150: Locale<'static> = Locale {
+    lang: "en-150",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-AE` locale.
+pub static EN_AE: Locale<'static> = Locale {
+    lang: "en-AE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-AG` locale.
+pub static EN_AG: Locale<'static> = Locale {
+    lang: "en-AG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-AI` locale.
+pub static EN_AI: Locale<'static> = Locale {
+    lang: "en-AI",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-AS` locale.
+pub static EN_AS: Locale<'static> = Locale {
+    lang: "en-AS",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-AT` locale.
+pub static EN_AT: Locale<'static> = Locale {
+    lang: "en-AT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-AU` locale.
+pub static EN_AU: Locale<'static> = Locale {
+    lang: "en-AU",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["Su.", "M.", "Tu.", "W.", "Th.", "F.", "Sa."],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["am", "pm"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BB` locale.
+pub static EN_BB: Locale<'static> = Locale {
+    lang: "en-BB",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BE` locale.
+pub static EN_BE: Locale<'static> = Locale {
+    lang: "en-BE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "dd MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-BI` locale.
+pub static EN_BI: Locale<'static> = Locale {
+    lang: "en-BI",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BM` locale.
+pub static EN_BM: Locale<'static> = Locale {
+    lang: "en-BM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BS` locale.
+pub static EN_BS: Locale<'static> = Locale {
+    lang: "en-BS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BW` locale.
+pub static EN_BW: Locale<'static> = Locale {
+    lang: "en-BW",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "dd MMMM y", "dd MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-BZ` locale.
+pub static EN_BZ: Locale<'static> = Locale {
+    lang: "en-BZ",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "dd MMMM y", "dd-MMM-y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CA` locale.
+pub static EN_CA: Locale<'static> = Locale {
+    lang: "en-CA",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["am", "pm"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CC` locale.
+pub static EN_CC: Locale<'static> = Locale {
+    lang: "en-CC",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CH` locale.
+pub static EN_CH: Locale<'static> = Locale {
+    lang: "en-CH",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `en-CK` locale.
+pub static EN_CK: Locale<'static> = Locale {
+    lang: "en-CK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CM` locale.
+pub static EN_CM: Locale<'static> = Locale {
+    lang: "en-CM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CX` locale.
+pub static EN_CX: Locale<'static> = Locale {
+    lang: "en-CX",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CY` locale.
+pub static EN_CY: Locale<'static> = Locale {
+    lang: "en-CY",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-CZ` locale.
+pub static EN_CZ: Locale<'static> = Locale {
+    lang: "en-CZ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-DE` locale.
+pub static EN_DE: Locale<'static> = Locale {
+    lang: "en-DE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-DG` locale.
+pub static EN_DG: Locale<'static> = Locale {
+    lang: "en-DG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-DK` locale.
+pub static EN_DK: Locale<'static> = Locale {
+    lang: "en-DK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-DM` locale.
+pub static EN_DM: Locale<'static> = Locale {
+    lang: "en-DM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-Dsrt` locale.
+pub static EN_DSRT: Locale<'static> = Locale {
+    lang: "en-Dsrt",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𐐖𐐰𐑌𐐷𐐭𐐯𐑉𐐨",
+            "𐐙𐐯𐐺𐑉𐐭𐐯𐑉𐐨",
+            "𐐣𐐪𐑉𐐽",
+            "𐐁𐐹𐑉𐐮𐑊",
+            "𐐣𐐩",
+            "𐐖𐐭𐑌",
+            "𐐖𐐭𐑊𐐴",
+            "𐐂𐑀𐐲𐑅𐐻",
+            "𐐝𐐯𐐹𐐻𐐯𐑋𐐺𐐲𐑉",
+            "𐐉𐐿𐐻𐐬𐐺𐐲𐑉",
+            "𐐤𐐬𐑂𐐯𐑋𐐺𐐲𐑉",
+            "𐐔𐐨𐑅𐐯𐑋𐐺𐐲𐑉",
+        ],
+        [
+            "𐐖𐐰𐑌",
+            "𐐙𐐯𐐺",
+            "𐐣𐐪𐑉",
+            "𐐁𐐹𐑉",
+            "𐐣𐐩",
+            "𐐖𐐭𐑌",
+            "𐐖𐐭𐑊",
+            "𐐂𐑀",
+            "𐐝𐐯𐐹",
+            "𐐉𐐿𐐻",
+            "𐐤𐐬𐑂",
+            "𐐔𐐨𐑅",
+        ],
+        ["𐐖", "𐐙", "𐐣", "𐐁", "𐐣", "𐐖", "𐐖", "𐐂", "𐐝", "𐐉", "𐐤", "𐐔"],
+    ),
+    days: Locale::new_days(
+        [
+            "𐐝𐐲𐑌𐐼𐐩",
+            "𐐣𐐲𐑌𐐼𐐩",
+            "𐐓𐐭𐑆𐐼𐐩",
+            "𐐎𐐯𐑌𐑆𐐼𐐩",
+            "𐐛𐐲𐑉𐑆𐐼𐐩",
+            "𐐙𐑉𐐴𐐼𐐩",
+            "𐐝𐐰𐐻𐐲𐑉𐐼𐐩",
+        ],
+        ["𐐝𐐲𐑌", "𐐣𐐲𐑌", "𐐓𐐭𐑆", "𐐎𐐯𐑌", "𐐛𐐲𐑉", "𐐙𐑉𐐴", "𐐝𐐰𐐻"],
+        ["𐐝𐐲𐑌", "𐐣𐐲𐑌", "𐐓𐐭𐑆", "𐐎𐐯𐑌", "𐐛𐐲𐑉", "𐐙𐑉𐐴", "𐐝𐐰𐐻"],
+        ["𐐝", "𐐣", "𐐓", "𐐎", "𐐛", "𐐙", "𐐝"],
+    ),
+    day_periods: Locale::new_day_periods(["𐐈𐐣", "𐐑𐐣"], ["𐐈𐐣", "𐐑𐐣"], ["𐐈𐐣", "𐐑𐐣"]),
+    eras: Locale::new_eras("𐐒𐐗", "𐐈𐐔"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-EE` locale.
+pub static EN_EE: Locale<'static> = Locale {
+    lang: "en-EE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-ER` locale.
+pub static EN_ER: Locale<'static> = Locale {
+    lang: "en-ER",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-ES` locale.
+pub static EN_ES: Locale<'static> = Locale {
+    lang: "en-ES",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-FI` locale.
+pub static EN_FI: Locale<'static> = Locale {
+    lang: "en-FI",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("H.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-FJ` locale.
+pub static EN_FJ: Locale<'static> = Locale {
+    lang: "en-FJ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-FK` locale.
+pub static EN_FK: Locale<'static> = Locale {
+    lang: "en-FK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-FM` locale.
+pub static EN_FM: Locale<'static> = Locale {
+    lang: "en-FM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-FR` locale.
+pub static EN_FR: Locale<'static> = Locale {
+    lang: "en-FR",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-GB` locale.
+pub static EN_GB: Locale<'static> = Locale {
+    lang: "en-GB",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GD` locale.
+pub static EN_GD: Locale<'static> = Locale {
+    lang: "en-GD",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GE` locale.
+pub static EN_GE: Locale<'static> = Locale {
+    lang: "en-GE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-GG` locale.
+pub static EN_GG: Locale<'static> = Locale {
+    lang: "en-GG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GH` locale.
+pub static EN_GH: Locale<'static> = Locale {
+    lang: "en-GH",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GI` locale.
+pub static EN_GI: Locale<'static> = Locale {
+    lang: "en-GI",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GM` locale.
+pub static EN_GM: Locale<'static> = Locale {
+    lang: "en-GM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GS` locale.
+pub static EN_GS: Locale<'static> = Locale {
+    lang: "en-GS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GU` locale.
+pub static EN_GU: Locale<'static> = Locale {
+    lang: "en-GU",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-GY` locale.
+pub static EN_GY: Locale<'static> = Locale {
+    lang: "en-GY",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-HK` locale.
+pub static EN_HK: Locale<'static> = Locale {
+    lang: "en-HK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-HU` locale.
+pub static EN_HU: Locale<'static> = Locale {
+    lang: "en-HU",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-ID` locale.
+pub static EN_ID: Locale<'static> = Locale {
+    lang: "en-ID",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-IE` locale.
+pub static EN_IE: Locale<'static> = Locale {
+    lang: "en-IE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-IL` locale.
+pub static EN_IL: Locale<'static> = Locale {
+    lang: "en-IL",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-IM` locale.
+pub static EN_IM: Locale<'static> = Locale {
+    lang: "en-IM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-IN` locale.
+pub static EN_IN: Locale<'static> = Locale {
+    lang: "en-IN",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-IO` locale.
+pub static EN_IO: Locale<'static> = Locale {
+    lang: "en-IO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-IT` locale.
+pub static EN_IT: Locale<'static> = Locale {
+    lang: "en-IT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-JE` locale.
+pub static EN_JE: Locale<'static> = Locale {
+    lang: "en-JE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-JM` locale.
+pub static EN_JM: Locale<'static> = Locale {
+    lang: "en-JM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-JP` locale.
+pub static EN_JP: Locale<'static> = Locale {
+    lang: "en-JP",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "y/MM/dd"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-KE` locale.
+pub static EN_KE: Locale<'static> = Locale {
+    lang: "en-KE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-KI` locale.
+pub static EN_KI: Locale<'static> = Locale {
+    lang: "en-KI",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-KN` locale.
+pub static EN_KN: Locale<'static> = Locale {
+    lang: "en-KN",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-KY` locale.
+pub static EN_KY: Locale<'static> = Locale {
+    lang: "en-KY",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-LC` locale.
+pub static EN_LC: Locale<'static> = Locale {
+    lang: "en-LC",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-LR` locale.
+pub static EN_LR: Locale<'static> = Locale {
+    lang: "en-LR",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-LS` locale.
+pub static EN_LS: Locale<'static> = Locale {
+    lang: "en-LS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-LT` locale.
+pub static EN_LT: Locale<'static> = Locale {
+    lang: "en-LT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-LV` locale.
+pub static EN_LV: Locale<'static> = Locale {
+    lang: "en-LV",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-MG` locale.
+pub static EN_MG: Locale<'static> = Locale {
+    lang: "en-MG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MH` locale.
+pub static EN_MH: Locale<'static> = Locale {
+    lang: "en-MH",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MO` locale.
+pub static EN_MO: Locale<'static> = Locale {
+    lang: "en-MO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MP` locale.
+pub static EN_MP: Locale<'static> = Locale {
+    lang: "en-MP",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MS` locale.
+pub static EN_MS: Locale<'static> = Locale {
+    lang: "en-MS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MT` locale.
+pub static EN_MT: Locale<'static> = Locale {
+    lang: "en-MT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "dd MMMM y", "dd MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MU` locale.
+pub static EN_MU: Locale<'static> = Locale {
+    lang: "en-MU",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MV` locale.
+pub static EN_MV: Locale<'static> = Locale {
+    lang: "en-MV",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "dd-MM-y", "d-M-yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MW` locale.
+pub static EN_MW: Locale<'static> = Locale {
+    lang: "en-MW",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-MY` locale.
+pub static EN_MY: Locale<'static> = Locale {
+    lang: "en-MY",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NA` locale.
+pub static EN_NA: Locale<'static> = Locale {
+    lang: "en-NA",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NF` locale.
+pub static EN_NF: Locale<'static> = Locale {
+    lang: "en-NF",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NG` locale.
+pub static EN_NG: Locale<'static> = Locale {
+    lang: "en-NG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NL` locale.
+pub static EN_NL: Locale<'static> = Locale {
+    lang: "en-NL",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-NO` locale.
+pub static EN_NO: Locale<'static> = Locale {
+    lang: "en-NO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-NR` locale.
+pub static EN_NR: Locale<'static> = Locale {
+    lang: "en-NR",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NU` locale.
+pub static EN_NU: Locale<'static> = Locale {
+    lang: "en-NU",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-NZ` locale.
+pub static EN_NZ: Locale<'static> = Locale {
+    lang: "en-NZ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PG` locale.
+pub static EN_PG: Locale<'static> = Locale {
+    lang: "en-PG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PH` locale.
+pub static EN_PH: Locale<'static> = Locale {
+    lang: "en-PH",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PK` locale.
+pub static EN_PK: Locale<'static> = Locale {
+    lang: "en-PK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "dd-MMM-y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PL` locale.
+pub static EN_PL: Locale<'static> = Locale {
+    lang: "en-PL",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-PN` locale.
+pub static EN_PN: Locale<'static> = Locale {
+    lang: "en-PN",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PR` locale.
+pub static EN_PR: Locale<'static> = Locale {
+    lang: "en-PR",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-PT` locale.
+pub static EN_PT: Locale<'static> = Locale {
+    lang: "en-PT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-PW` locale.
+pub static EN_PW: Locale<'static> = Locale {
+    lang: "en-PW",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-RO` locale.
+pub static EN_RO: Locale<'static> = Locale {
+    lang: "en-RO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-RW` locale.
+pub static EN_RW: Locale<'static> = Locale {
+    lang: "en-RW",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SB` locale.
+pub static EN_SB: Locale<'static> = Locale {
+    lang: "en-SB",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SC` locale.
+pub static EN_SC: Locale<'static> = Locale {
+    lang: "en-SC",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SD` locale.
+pub static EN_SD: Locale<'static> = Locale {
+    lang: "en-SD",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SE` locale.
+pub static EN_SE: Locale<'static> = Locale {
+    lang: "en-SE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-SG` locale.
+pub static EN_SG: Locale<'static> = Locale {
+    lang: "en-SG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SH` locale.
+pub static EN_SH: Locale<'static> = Locale {
+    lang: "en-SH",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SI` locale.
+pub static EN_SI: Locale<'static> = Locale {
+    lang: "en-SI",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `en-SK` locale.
+pub static EN_SK: Locale<'static> = Locale {
+    lang: "en-SK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-SL` locale.
+pub static EN_SL: Locale<'static> = Locale {
+    lang: "en-SL",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SS` locale.
+pub static EN_SS: Locale<'static> = Locale {
+    lang: "en-SS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SX` locale.
+pub static EN_SX: Locale<'static> = Locale {
+    lang: "en-SX",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-SZ` locale.
+pub static EN_SZ: Locale<'static> = Locale {
+    lang: "en-SZ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-Shaw` locale.
+pub static EN_SHAW: Locale<'static> = Locale {
+    lang: "en-Shaw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "·𐑡𐑨𐑙𐑘𐑭𐑢𐑺𐑰",
+            "·𐑓𐑧𐑚𐑘𐑵𐑢𐑺𐑰",
+            "·𐑥𐑸𐑗",
+            "·𐑱𐑐𐑮𐑭𐑤",
+            "·𐑥𐑱",
+            "·𐑡𐑵𐑯",
+            "·𐑡𐑫𐑤𐑲",
+            "·𐑪𐑜𐑭𐑕𐑑",
+            "·𐑕𐑧𐑐𐑑𐑧𐑥𐑚𐑸",
+            "·𐑷𐑒𐑑𐑴𐑚𐑸",
+            "·𐑯𐑴𐑝𐑧𐑥𐑚𐑸",
+            "·𐑛𐑭𐑕𐑧𐑥𐑚𐑸",
+        ],
+        [
+            "·𐑡𐑨",
+            "·𐑓𐑧",
+            "·𐑥𐑸",
+            "·𐑱𐑐",
+            "·𐑥𐑱",
+            "·𐑡𐑵",
+            "·𐑡𐑫",
+            "·𐑪𐑜",
+            "·𐑕𐑧",
+            "·𐑷𐑒",
+            "·𐑯𐑴",
+            "·𐑛𐑭",
+        ],
+        ["𐑡", "𐑓", "𐑥", "𐑱", "𐑥", "𐑡", "𐑡", "𐑪", "𐑕", "𐑷", "𐑯", "𐑛"],
+    ),
+    days: Locale::new_days(
+        [
+            "·𐑕𐑭𐑙𐑛𐑱",
+            "·𐑥𐑭𐑙𐑛𐑱",
+            "·𐑑𐑵𐑟𐑛𐑱",
+            "·𐑢𐑧𐑙𐑟𐑛𐑱",
+            "·𐑔𐑻𐑟𐑛𐑱",
+            "·𐑓𐑮𐑲𐑛𐑱",
+            "·𐑕𐑨𐑛𐑻𐑛𐑱",
+        ],
+        ["·𐑕𐑭", "·𐑥𐑭", "·𐑑𐑵", "·𐑢𐑧", "·𐑔𐑻", "·𐑓𐑮", "·𐑕𐑨"],
+        ["·𐑕𐑭", "·𐑥𐑭", "·𐑑𐑵", "·𐑢𐑧", "·𐑔𐑻", "·𐑓𐑮", "·𐑕𐑨"],
+        ["𐑕", "𐑥", "𐑑", "𐑢", "𐑔", "𐑓", "𐑕"],
+    ),
+    day_periods: Locale::new_day_periods(["𐑨𐑥", "𐑐𐑥"], ["𐑨𐑥", "𐑐𐑥"], ["𐑨𐑥", "𐑐𐑥"]),
+    eras: Locale::new_eras("𐑚·𐑒", "𐑨𐑛"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TC` locale.
+pub static EN_TC: Locale<'static> = Locale {
+    lang: "en-TC",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TK` locale.
+pub static EN_TK: Locale<'static> = Locale {
+    lang: "en-TK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TO` locale.
+pub static EN_TO: Locale<'static> = Locale {
+    lang: "en-TO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TT` locale.
+pub static EN_TT: Locale<'static> = Locale {
+    lang: "en-TT",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TV` locale.
+pub static EN_TV: Locale<'static> = Locale {
+    lang: "en-TV",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-TZ` locale.
+pub static EN_TZ: Locale<'static> = Locale {
+    lang: "en-TZ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-UA` locale.
+pub static EN_UA: Locale<'static> = Locale {
+    lang: "en-UA",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-UG` locale.
+pub static EN_UG: Locale<'static> = Locale {
+    lang: "en-UG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-UM` locale.
+pub static EN_UM: Locale<'static> = Locale {
+    lang: "en-UM",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-VC` locale.
+pub static EN_VC: Locale<'static> = Locale {
+    lang: "en-VC",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-VG` locale.
+pub static EN_VG: Locale<'static> = Locale {
+    lang: "en-VG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-VI` locale.
+pub static EN_VI: Locale<'static> = Locale {
+    lang: "en-VI",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-VU` locale.
+pub static EN_VU: Locale<'static> = Locale {
+    lang: "en-VU",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-WS` locale.
+pub static EN_WS: Locale<'static> = Locale {
+    lang: "en-WS",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-ZA` locale.
+pub static EN_ZA: Locale<'static> = Locale {
+    lang: "en-ZA",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "dd MMMM y", "dd MMM y", "y/MM/dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `en-ZM` locale.
+pub static EN_ZM: Locale<'static> = Locale {
+    lang: "en-ZM",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `en-ZW` locale.
+pub static EN_ZW: Locale<'static> = Locale {
+    lang: "en-ZW",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "dd MMMM y", "dd MMM,y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunday",
+            "Monday",
+            "Tuesday",
+            "Wednesday",
+            "Thursday",
+            "Friday",
+            "Saturday",
+        ],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `eo` locale.
+pub static EO: Locale<'static> = Locale {
+    lang: "eo",
+    dates: Locale::new_patterns(
+        "EEEE, 'la' d-'a' 'de' MMMM y",
+        "y-MMMM-dd",
+        "y-MMM-dd",
+        "yy-MM-dd",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januaro",
+            "Februaro",
+            "Marto",
+            "Aprilo",
+            "Majo",
+            "Junio",
+            "Julio",
+            "Aŭgusto",
+            "Septembro",
+            "Oktobro",
+            "Novembro",
+            "Decembro",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aŭg", "Sep", "Okt", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanĉo", "lundo", "mardo", "merkredo", "ĵaŭdo", "vendredo", "sabato",
+        ],
+        ["di", "lu", "ma", "me", "ĵa", "ve", "sa"],
+        ["di", "lu", "ma", "me", "ĵa", "ve", "sa"],
+        ["d", "l", "m", "m", "ĵ", "v", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["atm", "ptm"], ["atm", "ptm"], ["a", "p"]),
+    eras: Locale::new_eras("a.n.e.", "n.e."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `es` locale.
+pub static ES: Locale<'static> = Locale {
+    lang: "es",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a. C.", "d. C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-419` locale.
+pub static ES_419: Locale<'static> = Locale {
+    lang: "es-419",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-AR` locale.
+pub static ES_AR: Locale<'static> = Locale {
+    lang: "es-AR",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-BO` locale.
+pub static ES_BO: Locale<'static> = Locale {
+    lang: "es-BO",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM 'de' y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-BR` locale.
+pub static ES_BR: Locale<'static> = Locale {
+    lang: "es-BR",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-BZ` locale.
+pub static ES_BZ: Locale<'static> = Locale {
+    lang: "es-BZ",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-CL` locale.
+pub static ES_CL: Locale<'static> = Locale {
+    lang: "es-CL",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd-MM-y",
+        "dd-MM-yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["do", "lu", "ma", "mi", "ju", "vi", "sá"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-CO` locale.
+pub static ES_CO: Locale<'static> = Locale {
+    lang: "es-CO",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d/MM/y",
+        "d/MM/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "m", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-CR` locale.
+pub static ES_CR: Locale<'static> = Locale {
+    lang: "es-CR",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `es-CU` locale.
+pub static ES_CU: Locale<'static> = Locale {
+    lang: "es-CU",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-DO` locale.
+pub static ES_DO: Locale<'static> = Locale {
+    lang: "es-DO",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-EA` locale.
+pub static ES_EA: Locale<'static> = Locale {
+    lang: "es-EA",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a. C.", "d. C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-EC` locale.
+pub static ES_EC: Locale<'static> = Locale {
+    lang: "es-EC",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-GQ` locale.
+pub static ES_GQ: Locale<'static> = Locale {
+    lang: "es-GQ",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a. C.", "d. C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-GT` locale.
+pub static ES_GT: Locale<'static> = Locale {
+    lang: "es-GT",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d/MM/y",
+        "d/MM/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-HN` locale.
+pub static ES_HN: Locale<'static> = Locale {
+    lang: "es-HN",
+    dates: Locale::new_patterns(
+        "EEEE dd 'de' MMMM 'de' y",
+        "dd 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-IC` locale.
+pub static ES_IC: Locale<'static> = Locale {
+    lang: "es-IC",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a. C.", "d. C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-MX` locale.
+pub static ES_MX: Locale<'static> = Locale {
+    lang: "es-MX",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a. m.", "p. m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-NI` locale.
+pub static ES_NI: Locale<'static> = Locale {
+    lang: "es-NI",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-PA` locale.
+pub static ES_PA: Locale<'static> = Locale {
+    lang: "es-PA",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "MM/dd/y",
+        "MM/dd/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-PE` locale.
+pub static ES_PE: Locale<'static> = Locale {
+    lang: "es-PE",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/MM/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "setiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene.", "feb.", "mar.", "abr.", "may.", "jun.", "jul.", "ago.", "set.", "oct.", "nov.",
+            "dic.",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-PH` locale.
+pub static ES_PH: Locale<'static> = Locale {
+    lang: "es-PH",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a. C.", "d. C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-PR` locale.
+pub static ES_PR: Locale<'static> = Locale {
+    lang: "es-PR",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "MM/dd/y",
+        "MM/dd/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-PY` locale.
+pub static ES_PY: Locale<'static> = Locale {
+    lang: "es-PY",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene.", "feb.", "mar.", "abr.", "may.", "jun.", "jul.", "ago.", "sept.", "oct.",
+            "nov.", "dic.",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["do", "lu", "ma", "mi", "ju", "vi", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-SV` locale.
+pub static ES_SV: Locale<'static> = Locale {
+    lang: "es-SV",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-US` locale.
+pub static ES_US: Locale<'static> = Locale {
+    lang: "es-US",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/y",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sept", "oct", "nov", "dic",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `es-UY` locale.
+pub static ES_UY: Locale<'static> = Locale {
+    lang: "es-UY",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "setiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene.", "feb.", "mar.", "abr.", "may.", "jun.", "jul.", "ago.", "set.", "oct.", "nov.",
+            "dic.",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["DO", "LU", "MA", "MI", "JU", "VI", "SA"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `es-VE` locale.
+pub static ES_VE: Locale<'static> = Locale {
+    lang: "es-VE",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d MMM y",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "enero",
+            "febrero",
+            "marzo",
+            "abril",
+            "mayo",
+            "junio",
+            "julio",
+            "agosto",
+            "septiembre",
+            "octubre",
+            "noviembre",
+            "diciembre",
+        ],
+        [
+            "ene.", "feb.", "mar.", "abr.", "may.", "jun.", "jul.", "ago.", "sept.", "oct.",
+            "nov.", "dic.",
+        ],
+        ["E", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "lunes",
+            "martes",
+            "miércoles",
+            "jueves",
+            "viernes",
+            "sábado",
+        ],
+        ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
+        ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `et` locale.
+pub static ET: Locale<'static> = Locale {
+    lang: "et",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "jaanuar",
+            "veebruar",
+            "märts",
+            "aprill",
+            "mai",
+            "juuni",
+            "juuli",
+            "august",
+            "september",
+            "oktoober",
+            "november",
+            "detsember",
+        ],
+        [
+            "jaan", "veebr", "märts", "apr", "mai", "juuni", "juuli", "aug", "sept", "okt", "nov",
+            "dets",
+        ],
+        ["J", "V", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "pühapäev",
+            "esmaspäev",
+            "teisipäev",
+            "kolmapäev",
+            "neljapäev",
+            "reede",
+            "laupäev",
+        ],
+        ["P", "E", "T", "K", "N", "R", "L"],
+        ["P", "E", "T", "K", "N", "R", "L"],
+        ["P", "E", "T", "K", "N", "R", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("eKr", "pKr"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `eu` locale.
+pub static EU: Locale<'static> = Locale {
+    lang: "eu",
+    dates: Locale::new_patterns(
+        "y('e')'ko' MMMM'ren' d('a'), EEEE",
+        "y('e')'ko' MMMM'ren' d('a')",
+        "y('e')'ko' MMM d('a')",
+        "yy/M/d",
+    ),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} ({0})", "{1} ({0})", "{1} ({0})", "{1} ({0})"),
+    months: Locale::new_months(
+        [
+            "urtarrila",
+            "otsaila",
+            "martxoa",
+            "apirila",
+            "maiatza",
+            "ekaina",
+            "uztaila",
+            "abuztua",
+            "iraila",
+            "urria",
+            "azaroa",
+            "abendua",
+        ],
+        [
+            "urt.", "ots.", "mar.", "api.", "mai.", "eka.", "uzt.", "abu.", "ira.", "urr.", "aza.",
+            "abe.",
+        ],
+        ["U", "O", "M", "A", "M", "E", "U", "A", "I", "U", "A", "A"],
+    ),
+    days: Locale::new_days(
+        [
+            "igandea",
+            "astelehena",
+            "asteartea",
+            "asteazkena",
+            "osteguna",
+            "ostirala",
+            "larunbata",
+        ],
+        ["ig.", "al.", "ar.", "az.", "og.", "or.", "lr."],
+        ["ig.", "al.", "ar.", "az.", "og.", "or.", "lr."],
+        ["I", "A", "A", "A", "O", "O", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["g", "a"]),
+    eras: Locale::new_eras("K.a.", "K.o."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ewo` locale.
+pub static EWO: Locale<'static> = Locale {
+    lang: "ewo",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ngɔn osú",
+            "ngɔn bɛ̌",
+            "ngɔn lála",
+            "ngɔn nyina",
+            "ngɔn tána",
+            "ngɔn saməna",
+            "ngɔn zamgbála",
+            "ngɔn mwom",
+            "ngɔn ebulú",
+            "ngɔn awóm",
+            "ngɔn awóm ai dziá",
+            "ngɔn awóm ai bɛ̌",
+        ],
+        [
+            "ngo", "ngb", "ngl", "ngn", "ngt", "ngs", "ngz", "ngm", "nge", "nga", "ngad", "ngab",
+        ],
+        ["o", "b", "l", "n", "t", "s", "z", "m", "e", "a", "d", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "sɔ́ndɔ",
+            "mɔ́ndi",
+            "sɔ́ndɔ məlú mə́bɛ̌",
+            "sɔ́ndɔ məlú mə́lɛ́",
+            "sɔ́ndɔ məlú mə́nyi",
+            "fúladé",
+            "séradé",
+        ],
+        ["sɔ́n", "mɔ́n", "smb", "sml", "smn", "fúl", "sér"],
+        ["sɔ́n", "mɔ́n", "smb", "sml", "smn", "fúl", "sér"],
+        ["s", "m", "s", "s", "s", "f", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["kíkíríg", "ngəgógəle"],
+        ["kíkíríg", "ngəgógəle"],
+        ["kíkíríg", "ngəgógəle"],
+    ),
+    eras: Locale::new_eras("oyk", "ayk"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fa` locale.
+pub static FA: Locale<'static> = Locale {
+    lang: "fa",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "y/M/d"),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss (z)", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}، ساعت {0}", "{1}، ساعت {0}", "{1}،‏ {0}", "{1}،‏ {0}"),
+    months: Locale::new_months(
+        [
+            "ژانویهٔ",
+            "فوریهٔ",
+            "مارس",
+            "آوریل",
+            "مهٔ",
+            "ژوئن",
+            "ژوئیهٔ",
+            "اوت",
+            "سپتامبر",
+            "اکتبر",
+            "نوامبر",
+            "دسامبر",
+        ],
+        [
+            "ژانویه",
+            "فوریه",
+            "مارس",
+            "آوریل",
+            "مه",
+            "ژوئن",
+            "ژوئیه",
+            "اوت",
+            "سپتامبر",
+            "اکتبر",
+            "نوامبر",
+            "دسامبر",
+        ],
+        ["ژ", "ف", "م", "آ", "م", "ژ", "ژ", "ا", "س", "ا", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "یکشنبه",
+            "دوشنبه",
+            "سه‌شنبه",
+            "چهارشنبه",
+            "پنجشنبه",
+            "جمعه",
+            "شنبه",
+        ],
+        [
+            "یکشنبه",
+            "دوشنبه",
+            "سه‌شنبه",
+            "چهارشنبه",
+            "پنجشنبه",
+            "جمعه",
+            "شنبه",
+        ],
+        ["۱ش", "۲ش", "۳ش", "۴ش", "۵ش", "ج", "ش"],
+        ["ی", "د", "س", "چ", "پ", "ج", "ش"],
+    ),
+    day_periods: Locale::new_day_periods(["قبل‌ازظهر", "بعدازظهر"], ["ق.ظ.", "ب.ظ."], ["ق", "ب"]),
+    eras: Locale::new_eras("ق.م.", "م."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `fa-AF` locale.
+pub static FA_AF: Locale<'static> = Locale {
+    lang: "fa-AF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "y/M/d"),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss (z)", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}، ساعت {0}", "{1}، ساعت {0}", "{1}،‏ {0}", "{1}،‏ {0}"),
+    months: Locale::new_months(
+        [
+            "جنوری",
+            "فبروری",
+            "مارچ",
+            "اپریل",
+            "می",
+            "جون",
+            "جولای",
+            "اگست",
+            "سپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنو",
+            "فبروری",
+            "مارچ",
+            "اپریل",
+            "می",
+            "جون",
+            "جول",
+            "اگست",
+            "سپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسم",
+        ],
+        ["ج", "ف", "م", "ا", "م", "ج", "ج", "ا", "س", "ا", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        [
+            "یکشنبه",
+            "دوشنبه",
+            "سه‌شنبه",
+            "چهارشنبه",
+            "پنجشنبه",
+            "جمعه",
+            "شنبه",
+        ],
+        [
+            "یکشنبه",
+            "دوشنبه",
+            "سه‌شنبه",
+            "چهارشنبه",
+            "پنجشنبه",
+            "جمعه",
+            "شنبه",
+        ],
+        ["۱ش", "۲ش", "۳ش", "۴ش", "۵ش", "ج", "ش"],
+        ["ی", "د", "س", "چ", "پ", "ج", "ش"],
+    ),
+    day_periods: Locale::new_day_periods(["قبل‌ازظهر", "بعدازظهر"], ["ق.ظ.", "ب.ظ."], ["ق", "ب"]),
+    eras: Locale::new_eras("ق.م.", "م."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ff` locale.
+pub static FF: Locale<'static> = Locale {
+    lang: "ff",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Adlm` locale.
+pub static FF_ADLM: Locale<'static> = Locale {
+    lang: "ff-Adlm",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-BF` locale.
+pub static FF_ADLM_BF: Locale<'static> = Locale {
+    lang: "ff-Adlm-BF",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-CM` locale.
+pub static FF_ADLM_CM: Locale<'static> = Locale {
+    lang: "ff-Adlm-CM",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-GH` locale.
+pub static FF_ADLM_GH: Locale<'static> = Locale {
+    lang: "ff-Adlm-GH",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-GM` locale.
+pub static FF_ADLM_GM: Locale<'static> = Locale {
+    lang: "ff-Adlm-GM",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-GW` locale.
+pub static FF_ADLM_GW: Locale<'static> = Locale {
+    lang: "ff-Adlm-GW",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-LR` locale.
+pub static FF_ADLM_LR: Locale<'static> = Locale {
+    lang: "ff-Adlm-LR",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-MR` locale.
+pub static FF_ADLM_MR: Locale<'static> = Locale {
+    lang: "ff-Adlm-MR",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-NE` locale.
+pub static FF_ADLM_NE: Locale<'static> = Locale {
+    lang: "ff-Adlm-NE",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-NG` locale.
+pub static FF_ADLM_NG: Locale<'static> = Locale {
+    lang: "ff-Adlm-NG",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-SL` locale.
+pub static FF_ADLM_SL: Locale<'static> = Locale {
+    lang: "ff-Adlm-SL",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Adlm-SN` locale.
+pub static FF_ADLM_SN: Locale<'static> = Locale {
+    lang: "ff-Adlm-SN",
+    dates: Locale::new_patterns("EEEE d MMMM⹁ y", "d MMMM⹁ y", "d MMM⹁ y", "d-M-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        [
+            "𞤅𞤭𞥅𞤤𞤮",
+            "𞤕𞤮𞤤𞤼𞤮",
+            "𞤐𞤦𞤮𞥅𞤴𞤮",
+            "𞤅𞤫𞥅𞤼𞤮",
+            "𞤁𞤵𞥅𞤶𞤮",
+            "𞤑𞤮𞤪𞤧𞤮",
+            "𞤃𞤮𞤪𞤧𞤮",
+            "𞤔𞤵𞤳𞤮",
+            "𞤅𞤭𞤤𞤼𞤮",
+            "𞤒𞤢𞤪𞤳𞤮",
+            "𞤔𞤮𞤤𞤮",
+            "𞤄𞤮𞤱𞤼𞤮",
+        ],
+        ["𞤅", "𞤕", "𞤄", "𞤅", "𞤁", "𞤑", "𞤃", "𞤔", "𞤅", "𞤒", "𞤔", "𞤄"],
+    ),
+    days: Locale::new_days(
+        [
+            "𞤈𞤫𞤬𞤦𞤭𞤪𞥆𞤫",
+            "𞤀𞥄𞤩𞤵𞤲𞥋𞤣𞤫",
+            "𞤃𞤢𞤱𞤦𞤢𞥄𞤪𞤫",
+            "𞤐𞤶𞤫𞤧𞤤𞤢𞥄𞤪𞤫",
+            "𞤐𞤢𞥄𞤧𞤢𞥄𞤲𞤣𞤫",
+            "𞤃𞤢𞤱𞤲𞤣𞤫",
+            "𞤖𞤮𞤪𞤦𞤭𞤪𞥆𞤫",
+        ],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈𞤫𞤬", "𞤀𞥄𞤩𞤵", "𞤃𞤢𞤦", "𞤔𞤫𞤧", "𞤐𞤢𞥄𞤧", "𞤃𞤢𞤣", "𞤖𞤮𞤪"],
+        ["𞤈", "𞤀𞥄", "𞤃", "𞤔", "𞤐", "𞤃", "𞤖"],
+    ),
+    day_periods: Locale::new_day_periods(["𞤀𞤎", "𞤇𞤎"], ["𞤀𞤎", "𞤇𞤎"], ["𞤢", "𞤩"]),
+    eras: Locale::new_eras("𞤀𞤀𞤋", "𞤇𞤀𞤋"),
+    decimal: '.',
+    group: '⹁',
+};
+/// Data for `ff-Latn` locale.
+pub static FF_LATN: Locale<'static> = Locale {
+    lang: "ff-Latn",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-BF` locale.
+pub static FF_LATN_BF: Locale<'static> = Locale {
+    lang: "ff-Latn-BF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-CM` locale.
+pub static FF_LATN_CM: Locale<'static> = Locale {
+    lang: "ff-Latn-CM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-GH` locale.
+pub static FF_LATN_GH: Locale<'static> = Locale {
+    lang: "ff-Latn-GH",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-GM` locale.
+pub static FF_LATN_GM: Locale<'static> = Locale {
+    lang: "ff-Latn-GM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-GN` locale.
+pub static FF_LATN_GN: Locale<'static> = Locale {
+    lang: "ff-Latn-GN",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-GW` locale.
+pub static FF_LATN_GW: Locale<'static> = Locale {
+    lang: "ff-Latn-GW",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-LR` locale.
+pub static FF_LATN_LR: Locale<'static> = Locale {
+    lang: "ff-Latn-LR",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-MR` locale.
+pub static FF_LATN_MR: Locale<'static> = Locale {
+    lang: "ff-Latn-MR",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-NE` locale.
+pub static FF_LATN_NE: Locale<'static> = Locale {
+    lang: "ff-Latn-NE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-NG` locale.
+pub static FF_LATN_NG: Locale<'static> = Locale {
+    lang: "ff-Latn-NG",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ff-Latn-SL` locale.
+pub static FF_LATN_SL: Locale<'static> = Locale {
+    lang: "ff-Latn-SL",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siilo", "colte", "mbooy", "seeɗto", "duujal", "korse", "morso", "juko", "siilto",
+            "yarkomaa", "jolal", "bowte",
+        ],
+        [
+            "sii", "col", "mbo", "see", "duu", "kor", "mor", "juk", "slt", "yar", "jol", "bow",
+        ],
+        ["s", "c", "m", "s", "d", "k", "m", "j", "s", "y", "j", "b"],
+    ),
+    days: Locale::new_days(
+        [
+            "dewo",
+            "aaɓnde",
+            "mawbaare",
+            "njeslaare",
+            "naasaande",
+            "mawnde",
+            "hoore-biir",
+        ],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["dew", "aaɓ", "maw", "nje", "naa", "mwd", "hbi"],
+        ["d", "a", "m", "n", "n", "m", "h"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+        ["subaka", "kikiiɗe"],
+    ),
+    eras: Locale::new_eras("H-I", "C-I"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fi` locale.
+pub static FI: Locale<'static> = Locale {
+    lang: "fi",
+    dates: Locale::new_patterns("cccc d. MMMM y", "d. MMMM y", "d.M.y", "d.M.y"),
+    times: Locale::new_patterns("H.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "tammikuuta",
+            "helmikuuta",
+            "maaliskuuta",
+            "huhtikuuta",
+            "toukokuuta",
+            "kesäkuuta",
+            "heinäkuuta",
+            "elokuuta",
+            "syyskuuta",
+            "lokakuuta",
+            "marraskuuta",
+            "joulukuuta",
+        ],
+        [
+            "tammi", "helmi", "maalis", "huhti", "touko", "kesä", "heinä", "elo", "syys", "loka",
+            "marras", "joulu",
+        ],
+        ["T", "H", "M", "H", "T", "K", "H", "E", "S", "L", "M", "J"],
+    ),
+    days: Locale::new_days(
+        [
+            "sunnuntaina",
+            "maanantaina",
+            "tiistaina",
+            "keskiviikkona",
+            "torstaina",
+            "perjantaina",
+            "lauantaina",
+        ],
+        ["su", "ma", "ti", "ke", "to", "pe", "la"],
+        ["su", "ma", "ti", "ke", "to", "pe", "la"],
+        ["S", "M", "T", "K", "T", "P", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["ap.", "ip."], ["ap.", "ip."], ["ap.", "ip."]),
+    eras: Locale::new_eras("eKr.", "jKr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fil` locale.
+pub static FIL: Locale<'static> = Locale {
+    lang: "fil",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Enero",
+            "Pebrero",
+            "Marso",
+            "Abril",
+            "Mayo",
+            "Hunyo",
+            "Hulyo",
+            "Agosto",
+            "Setyembre",
+            "Oktubre",
+            "Nobyembre",
+            "Disyembre",
+        ],
+        [
+            "Ene", "Peb", "Mar", "Abr", "May", "Hun", "Hul", "Ago", "Set", "Okt", "Nob", "Dis",
+        ],
+        [
+            "Ene", "Peb", "Mar", "Abr", "May", "Hun", "Hul", "Ago", "Set", "Okt", "Nob", "Dis",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Linggo",
+            "Lunes",
+            "Martes",
+            "Miyerkules",
+            "Huwebes",
+            "Biyernes",
+            "Sabado",
+        ],
+        ["Lin", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab"],
+        ["Lin", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab"],
+        ["Lin", "Lun", "Mar", "Miy", "Huw", "Biy", "Sab"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `fo` locale.
+pub static FO: Locale<'static> = Locale {
+    lang: "fo",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "apríl",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "mai", "jun.", "jul.", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "sunnudagur",
+            "mánadagur",
+            "týsdagur",
+            "mikudagur",
+            "hósdagur",
+            "fríggjadagur",
+            "leygardagur",
+        ],
+        ["sun.", "mán.", "týs.", "mik.", "hós.", "frí.", "ley."],
+        ["su.", "má.", "tý.", "mi.", "hó.", "fr.", "le."],
+        ["S", "M", "T", "M", "H", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `fo-DK` locale.
+pub static FO_DK: Locale<'static> = Locale {
+    lang: "fo-DK",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "apríl",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "mai", "jun.", "jul.", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "sunnudagur",
+            "mánadagur",
+            "týsdagur",
+            "mikudagur",
+            "hósdagur",
+            "fríggjadagur",
+            "leygardagur",
+        ],
+        ["sun.", "mán.", "týs.", "mik.", "hós.", "frí.", "ley."],
+        ["su.", "má.", "tý.", "mi.", "hó.", "fr.", "le."],
+        ["S", "M", "T", "M", "H", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `fr` locale.
+pub static FR: Locale<'static> = Locale {
+    lang: "fr",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-BE` locale.
+pub static FR_BE: Locale<'static> = Locale {
+    lang: "fr-BE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/MM/yy"),
+    times: Locale::new_patterns(
+        "H 'h' mm 'min' ss 's' zzzz",
+        "HH:mm:ss z",
+        "HH:mm:ss",
+        "HH:mm",
+    ),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-BF` locale.
+pub static FR_BF: Locale<'static> = Locale {
+    lang: "fr-BF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-BI` locale.
+pub static FR_BI: Locale<'static> = Locale {
+    lang: "fr-BI",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-BJ` locale.
+pub static FR_BJ: Locale<'static> = Locale {
+    lang: "fr-BJ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-BL` locale.
+pub static FR_BL: Locale<'static> = Locale {
+    lang: "fr-BL",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CA` locale.
+pub static FR_CA: Locale<'static> = Locale {
+    lang: "fr-CA",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "y-MM-dd"),
+    times: Locale::new_patterns(
+        "HH 'h' mm 'min' ss 's' zzzz",
+        "HH 'h' mm 'min' ss 's' z",
+        "HH 'h' mm 'min' ss 's'",
+        "HH 'h' mm",
+    ),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juill.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a", "p"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CD` locale.
+pub static FR_CD: Locale<'static> = Locale {
+    lang: "fr-CD",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CF` locale.
+pub static FR_CF: Locale<'static> = Locale {
+    lang: "fr-CF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CG` locale.
+pub static FR_CG: Locale<'static> = Locale {
+    lang: "fr-CG",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CH` locale.
+pub static FR_CH: Locale<'static> = Locale {
+    lang: "fr-CH",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH.mm:ss 'h' zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CI` locale.
+pub static FR_CI: Locale<'static> = Locale {
+    lang: "fr-CI",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-CM` locale.
+pub static FR_CM: Locale<'static> = Locale {
+    lang: "fr-CM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["matin", "soir"], ["mat.", "soir"], ["mat.", "soir"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-DJ` locale.
+pub static FR_DJ: Locale<'static> = Locale {
+    lang: "fr-DJ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-DZ` locale.
+pub static FR_DZ: Locale<'static> = Locale {
+    lang: "fr-DZ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-GA` locale.
+pub static FR_GA: Locale<'static> = Locale {
+    lang: "fr-GA",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-GF` locale.
+pub static FR_GF: Locale<'static> = Locale {
+    lang: "fr-GF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-GN` locale.
+pub static FR_GN: Locale<'static> = Locale {
+    lang: "fr-GN",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-GP` locale.
+pub static FR_GP: Locale<'static> = Locale {
+    lang: "fr-GP",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-GQ` locale.
+pub static FR_GQ: Locale<'static> = Locale {
+    lang: "fr-GQ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-HT` locale.
+pub static FR_HT: Locale<'static> = Locale {
+    lang: "fr-HT",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-KM` locale.
+pub static FR_KM: Locale<'static> = Locale {
+    lang: "fr-KM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-LU` locale.
+pub static FR_LU: Locale<'static> = Locale {
+    lang: "fr-LU",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `fr-MA` locale.
+pub static FR_MA: Locale<'static> = Locale {
+    lang: "fr-MA",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "jan.", "fév.", "mar.", "avr.", "mai", "jui.", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `fr-MC` locale.
+pub static FR_MC: Locale<'static> = Locale {
+    lang: "fr-MC",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-MF` locale.
+pub static FR_MF: Locale<'static> = Locale {
+    lang: "fr-MF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-MG` locale.
+pub static FR_MG: Locale<'static> = Locale {
+    lang: "fr-MG",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-ML` locale.
+pub static FR_ML: Locale<'static> = Locale {
+    lang: "fr-ML",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-MQ` locale.
+pub static FR_MQ: Locale<'static> = Locale {
+    lang: "fr-MQ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-MR` locale.
+pub static FR_MR: Locale<'static> = Locale {
+    lang: "fr-MR",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-MU` locale.
+pub static FR_MU: Locale<'static> = Locale {
+    lang: "fr-MU",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-NC` locale.
+pub static FR_NC: Locale<'static> = Locale {
+    lang: "fr-NC",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-NE` locale.
+pub static FR_NE: Locale<'static> = Locale {
+    lang: "fr-NE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-PF` locale.
+pub static FR_PF: Locale<'static> = Locale {
+    lang: "fr-PF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-PM` locale.
+pub static FR_PM: Locale<'static> = Locale {
+    lang: "fr-PM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-RE` locale.
+pub static FR_RE: Locale<'static> = Locale {
+    lang: "fr-RE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-RW` locale.
+pub static FR_RW: Locale<'static> = Locale {
+    lang: "fr-RW",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-SC` locale.
+pub static FR_SC: Locale<'static> = Locale {
+    lang: "fr-SC",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-SN` locale.
+pub static FR_SN: Locale<'static> = Locale {
+    lang: "fr-SN",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-SY` locale.
+pub static FR_SY: Locale<'static> = Locale {
+    lang: "fr-SY",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-TD` locale.
+pub static FR_TD: Locale<'static> = Locale {
+    lang: "fr-TD",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-TG` locale.
+pub static FR_TG: Locale<'static> = Locale {
+    lang: "fr-TG",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-TN` locale.
+pub static FR_TN: Locale<'static> = Locale {
+    lang: "fr-TN",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-VU` locale.
+pub static FR_VU: Locale<'static> = Locale {
+    lang: "fr-VU",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-WF` locale.
+pub static FR_WF: Locale<'static> = Locale {
+    lang: "fr-WF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `fr-YT` locale.
+pub static FR_YT: Locale<'static> = Locale {
+    lang: "fr-YT",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `frr` locale.
+pub static FRR: Locale<'static> = Locale {
+    lang: "frr",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `fur` locale.
+pub static FUR: Locale<'static> = Locale {
+    lang: "fur",
+    dates: Locale::new_patterns(
+        "EEEE d 'di' MMMM 'dal' y",
+        "d 'di' MMMM 'dal' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Zenâr", "Fevrâr", "Març", "Avrîl", "Mai", "Jugn", "Lui", "Avost", "Setembar",
+            "Otubar", "Novembar", "Dicembar",
+        ],
+        [
+            "Zen", "Fev", "Mar", "Avr", "Mai", "Jug", "Lui", "Avo", "Set", "Otu", "Nov", "Dic",
+        ],
+        ["Z", "F", "M", "A", "M", "J", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domenie", "lunis", "martars", "miercus", "joibe", "vinars", "sabide",
+        ],
+        ["dom", "lun", "mar", "mie", "joi", "vin", "sab"],
+        ["dom", "lun", "mar", "mie", "joi", "vin", "sab"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.", "p."], ["a.", "p."], ["a.", "p."]),
+    eras: Locale::new_eras("pdC", "ddC"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `fy` locale.
+pub static FY: Locale<'static> = Locale {
+    lang: "fy",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jannewaris",
+            "Febrewaris",
+            "Maart",
+            "April",
+            "Maaie",
+            "Juny",
+            "July",
+            "Augustus",
+            "Septimber",
+            "Oktober",
+            "Novimber",
+            "Desimber",
+        ],
+        [
+            "Jan", "Feb", "Mrt", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "snein",
+            "moandei",
+            "tiisdei",
+            "woansdei",
+            "tongersdei",
+            "freed",
+            "sneon",
+        ],
+        ["si", "mo", "ti", "wo", "to", "fr", "so"],
+        ["si", "mo", "ti", "wo", "to", "fr", "so"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("f.Kr.", "n.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ga` locale.
+pub static GA: Locale<'static> = Locale {
+    lang: "ga",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Eanáir",
+            "Feabhra",
+            "Márta",
+            "Aibreán",
+            "Bealtaine",
+            "Meitheamh",
+            "Iúil",
+            "Lúnasa",
+            "Meán Fómhair",
+            "Deireadh Fómhair",
+            "Samhain",
+            "Nollaig",
+        ],
+        [
+            "Ean", "Feabh", "Márta", "Aib", "Beal", "Meith", "Iúil", "Lún", "MFómh", "DFómh",
+            "Samh", "Noll",
+        ],
+        ["E", "F", "M", "A", "B", "M", "I", "L", "M", "D", "S", "N"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dé Domhnaigh",
+            "Dé Luain",
+            "Dé Máirt",
+            "Dé Céadaoin",
+            "Déardaoin",
+            "Dé hAoine",
+            "Dé Sathairn",
+        ],
+        ["Domh", "Luan", "Máirt", "Céad", "Déar", "Aoine", "Sath"],
+        ["Do", "Lu", "Má", "Cé", "Dé", "Ao", "Sa"],
+        ["D", "L", "M", "C", "D", "A", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["r.n.", "i.n."], ["r.n.", "i.n."], ["r.n.", "i.n."]),
+    eras: Locale::new_eras("RC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ga-GB` locale.
+pub static GA_GB: Locale<'static> = Locale {
+    lang: "ga-GB",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Eanáir",
+            "Feabhra",
+            "Márta",
+            "Aibreán",
+            "Bealtaine",
+            "Meitheamh",
+            "Iúil",
+            "Lúnasa",
+            "Meán Fómhair",
+            "Deireadh Fómhair",
+            "Samhain",
+            "Nollaig",
+        ],
+        [
+            "Ean", "Feabh", "Márta", "Aib", "Beal", "Meith", "Iúil", "Lún", "MFómh", "DFómh",
+            "Samh", "Noll",
+        ],
+        ["E", "F", "M", "A", "B", "M", "I", "L", "M", "D", "S", "N"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dé Domhnaigh",
+            "Dé Luain",
+            "Dé Máirt",
+            "Dé Céadaoin",
+            "Déardaoin",
+            "Dé hAoine",
+            "Dé Sathairn",
+        ],
+        ["Domh", "Luan", "Máirt", "Céad", "Déar", "Aoine", "Sath"],
+        ["Do", "Lu", "Má", "Cé", "Dé", "Ao", "Sa"],
+        ["D", "L", "M", "C", "D", "A", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["r.n.", "i.n."], ["r.n.", "i.n."], ["r.n.", "i.n."]),
+    eras: Locale::new_eras("RC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gaa` locale.
+pub static GAA: Locale<'static> = Locale {
+    lang: "gaa",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Aharabata",
+            "Oflɔ",
+            "Otsokrikri",
+            "Abɛibe",
+            "Agbiɛnaa",
+            "Otukwajaŋ",
+            "Maawɛ",
+            "Manyawale",
+            "Gbo",
+            "Antɔŋ",
+            "Alemle",
+            "Afuabe",
+        ],
+        [
+            "Aharabata",
+            "Oflɔ",
+            "Otsokrikri",
+            "Abɛibe",
+            "Agbiɛnaa",
+            "Otukwajaŋ",
+            "Maawɛ",
+            "Manyawale",
+            "Gbo",
+            "Antɔŋ",
+            "Alemle",
+            "Afuabe",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Hɔgbaa", "Ju", "Jufɔ", "Shɔ", "Soo", "Sohaa", "Hɔɔ"],
+        ["Hɔgbaa", "Ju", "Jufɔ", "Shɔ", "Soo", "Sohaa", "Hɔɔ"],
+        ["Hɔgbaa", "Ju", "Jufɔ", "Shɔ", "Soo", "Sohaa", "Hɔɔ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["LEEBI", "SHWANE"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gd` locale.
+pub static GD: Locale<'static> = Locale {
+    lang: "gd",
+    dates: Locale::new_patterns(
+        "EEEE, d'mh' MMMM y",
+        "d'mh' MMMM y",
+        "d'mh' MMM y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "dhen Fhaoilleach",
+            "dhen Ghearran",
+            "dhen Mhàrt",
+            "dhen Ghiblean",
+            "dhen Chèitean",
+            "dhen Ògmhios",
+            "dhen Iuchar",
+            "dhen Lùnastal",
+            "dhen t-Sultain",
+            "dhen Dàmhair",
+            "dhen t-Samhain",
+            "dhen Dùbhlachd",
+        ],
+        [
+            "Faoi", "Gearr", "Màrt", "Gibl", "Cèit", "Ògmh", "Iuch", "Lùna", "Sult", "Dàmh",
+            "Samh", "Dùbh",
+        ],
+        ["F", "G", "M", "G", "C", "Ò", "I", "L", "S", "D", "S", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "DiDòmhnaich",
+            "DiLuain",
+            "DiMàirt",
+            "DiCiadain",
+            "DiarDaoin",
+            "DihAoine",
+            "DiSathairne",
+        ],
+        ["DiD", "DiL", "DiM", "DiC", "Dia", "Dih", "DiS"],
+        ["Dò", "Lu", "Mà", "Ci", "Da", "hA", "Sa"],
+        ["D", "L", "M", "C", "A", "H", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["m", "f"], ["m", "f"], ["m", "f"]),
+    eras: Locale::new_eras("RC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gez` locale.
+pub static GEZ: Locale<'static> = Locale {
+    lang: "gez",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gez-ER` locale.
+pub static GEZ_ER: Locale<'static> = Locale {
+    lang: "gez-ER",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gl` locale.
+pub static GL: Locale<'static> = Locale {
+    lang: "gl",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d 'de' MMM 'de' y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "xaneiro", "febreiro", "marzo", "abril", "maio", "xuño", "xullo", "agosto", "setembro",
+            "outubro", "novembro", "decembro",
+        ],
+        [
+            "xan.", "feb.", "mar.", "abr.", "maio", "xuño", "xul.", "ago.", "set.", "out.", "nov.",
+            "dec.",
+        ],
+        [
+            "x.", "f.", "m.", "a.", "m.", "x.", "x.", "a.", "s.", "o.", "n.", "d.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "luns",
+            "martes",
+            "mércores",
+            "xoves",
+            "venres",
+            "sábado",
+        ],
+        ["dom.", "luns", "mar.", "mér.", "xov.", "ven.", "sáb."],
+        ["do.", "lu.", "ma.", "mé.", "xo.", "ve.", "sá."],
+        ["d.", "l.", "m.", "m.", "x.", "v.", "s."],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `gn` locale.
+pub static GN: Locale<'static> = Locale {
+    lang: "gn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jasyteĩ",
+            "Jasykõi",
+            "Jasyapy",
+            "Jasyrundy",
+            "Jasypo",
+            "Jasypoteĩ",
+            "Jasypokõi",
+            "Jasypoapy",
+            "Jasyporundy",
+            "Jasypa",
+            "Jasypateĩ",
+            "Jasypakõi",
+        ],
+        [
+            "Jasyteĩ",
+            "Jasykõi",
+            "Jasyapy",
+            "Jasyrundy",
+            "Jasypo",
+            "Jasypoteĩ",
+            "Jasypokõi",
+            "Jasypoapy",
+            "Jasyporundy",
+            "Jasypa",
+            "Jasypateĩ",
+            "Jasypakõi",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Arateĩ",
+            "Arakõi",
+            "Araapy",
+            "Ararundy",
+            "Arapo",
+            "Arapoteĩ",
+            "Arapokõi",
+        ],
+        [
+            "Arateĩ",
+            "Arakõi",
+            "Araapy",
+            "Ararundy",
+            "Arapo",
+            "Arapoteĩ",
+            "Arapokõi",
+        ],
+        [
+            "Arateĩ",
+            "Arakõi",
+            "Araapy",
+            "Ararundy",
+            "Arapo",
+            "Arapoteĩ",
+            "Arapokõi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gsw` locale.
+pub static GSW: Locale<'static> = Locale {
+    lang: "gsw",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "Auguscht",
+            "Septämber",
+            "Oktoober",
+            "Novämber",
+            "Dezämber",
+        ],
+        [
+            "Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunntig",
+            "Määntig",
+            "Ziischtig",
+            "Mittwuch",
+            "Dunschtig",
+            "Friitig",
+            "Samschtig",
+        ],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["am Vormittag", "am Namittag"],
+        ["vorm.", "nam."],
+        ["vorm.", "nam."],
+    ),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `gsw-FR` locale.
+pub static GSW_FR: Locale<'static> = Locale {
+    lang: "gsw-FR",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "Auguscht",
+            "Septämber",
+            "Oktoober",
+            "Novämber",
+            "Dezämber",
+        ],
+        [
+            "Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunntig",
+            "Määntig",
+            "Ziischtig",
+            "Mittwuch",
+            "Dunschtig",
+            "Friitig",
+            "Samschtig",
+        ],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["am Vormittag", "am Namittag"],
+        ["vorm.", "nam."],
+        ["vorm.", "nam."],
+    ),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `gsw-LI` locale.
+pub static GSW_LI: Locale<'static> = Locale {
+    lang: "gsw-LI",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "dd.MM.y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "Auguscht",
+            "Septämber",
+            "Oktoober",
+            "Novämber",
+            "Dezämber",
+        ],
+        [
+            "Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunntig",
+            "Määntig",
+            "Ziischtig",
+            "Mittwuch",
+            "Dunschtig",
+            "Friitig",
+            "Samschtig",
+        ],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["Su.", "Mä.", "Zi.", "Mi.", "Du.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["am Vormittag", "am Namittag"],
+        ["vorm.", "nam."],
+        ["vorm.", "nam."],
+    ),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `gu` locale.
+pub static GU: Locale<'static> = Locale {
+    lang: "gu",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("hh:mm:ss a zzzz", "hh:mm:ss a z", "hh:mm:ss a", "hh:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "જાન્યુઆરી",
+            "ફેબ્રુઆરી",
+            "માર્ચ",
+            "એપ્રિલ",
+            "મે",
+            "જૂન",
+            "જુલાઈ",
+            "ઑગસ્ટ",
+            "સપ્ટેમ્બર",
+            "ઑક્ટોબર",
+            "નવેમ્બર",
+            "ડિસેમ્બર",
+        ],
+        [
+            "જાન્યુ",
+            "ફેબ્રુ",
+            "માર્ચ",
+            "એપ્રિલ",
+            "મે",
+            "જૂન",
+            "જુલાઈ",
+            "ઑગસ્ટ",
+            "સપ્ટે",
+            "ઑક્ટો",
+            "નવે",
+            "ડિસે",
+        ],
+        [
+            "જા", "ફે", "મા", "એ", "મે", "જૂ", "જુ", "ઑ", "સ", "ઑ", "ન", "ડિ",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "રવિવાર",
+            "સોમવાર",
+            "મંગળવાર",
+            "બુધવાર",
+            "ગુરુવાર",
+            "શુક્રવાર",
+            "શનિવાર",
+        ],
+        ["રવિ", "સોમ", "મંગળ", "બુધ", "ગુરુ", "શુક્ર", "શનિ"],
+        ["ર", "સો", "મં", "બુ", "ગુ", "શુ", "શ"],
+        ["ર", "સો", "મં", "બુ", "ગુ", "શુ", "શ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ઈ.સ.પૂર્વે", "ઈ.સ."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `guz` locale.
+pub static GUZ: Locale<'static> = Locale {
+    lang: "guz",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Chanuari", "Feburari", "Machi", "Apiriri", "Mei", "Juni", "Chulai", "Agosti",
+            "Septemba", "Okitoba", "Nobemba", "Disemba",
+        ],
+        [
+            "Can", "Feb", "Mac", "Apr", "Mei", "Jun", "Cul", "Agt", "Sep", "Okt", "Nob", "Dis",
+        ],
+        ["C", "F", "M", "A", "M", "J", "C", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Chumapiri",
+            "Chumatato",
+            "Chumaine",
+            "Chumatano",
+            "Aramisi",
+            "Ichuma",
+            "Esabato",
+        ],
+        ["Cpr", "Ctt", "Cmn", "Cmt", "Ars", "Icm", "Est"],
+        ["Cpr", "Ctt", "Cmn", "Cmt", "Ars", "Icm", "Est"],
+        ["C", "C", "C", "C", "A", "I", "E"],
+    ),
+    day_periods: Locale::new_day_periods(["Mambia", "Mog"], ["Ma", "Mo"], ["Ma", "Mo"]),
+    eras: Locale::new_eras("YA", "YK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `gv` locale.
+pub static GV: Locale<'static> = Locale {
+    lang: "gv",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jerrey-geuree",
+            "Toshiaght-arree",
+            "Mayrnt",
+            "Averil",
+            "Boaldyn",
+            "Mean-souree",
+            "Jerrey-souree",
+            "Luanistyn",
+            "Mean-fouyir",
+            "Jerrey-fouyir",
+            "Mee Houney",
+            "Mee ny Nollick",
+        ],
+        [
+            "J-guer",
+            "T-arree",
+            "Mayrnt",
+            "Avrril",
+            "Boaldyn",
+            "M-souree",
+            "J-souree",
+            "Luanistyn",
+            "M-fouyir",
+            "J-fouyir",
+            "M-Houney",
+            "M-Nollick",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Jedoonee", "Jelhein", "Jemayrt", "Jercean", "Jerdein", "Jeheiney", "Jesarn",
+        ],
+        ["Jed", "Jel", "Jem", "Jerc", "Jerd", "Jeh", "Jes"],
+        ["Jed", "Jel", "Jem", "Jerc", "Jerd", "Jeh", "Jes"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("RC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ha` locale.
+pub static HA: Locale<'static> = Locale {
+    lang: "ha",
+    dates: Locale::new_patterns("EEEE d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janairu",
+            "Faburairu",
+            "Maris",
+            "Afirilu",
+            "Mayu",
+            "Yuni",
+            "Yuli",
+            "Agusta",
+            "Satumba",
+            "Oktoba",
+            "Nuwamba",
+            "Disamba",
+        ],
+        [
+            "Jan", "Fab", "Mar", "Afi", "May", "Yun", "Yul", "Agu", "Sat", "Okt", "Nuw", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "Y", "Y", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Lahadi", "Litinin", "Talata", "Laraba", "Alhamis", "Jummaʼa", "Asabar",
+        ],
+        ["Lah", "Lit", "Tal", "Lar", "Alh", "Jum", "Asa"],
+        ["Lh", "Li", "Ta", "Lr", "Al", "Ju", "As"],
+        ["L", "L", "T", "L", "A", "J", "A"],
+    ),
+    day_periods: Locale::new_day_periods(["Safiya", "Yamma"], ["SF", "YM"], ["SF", "YM"]),
+    eras: Locale::new_eras("K.H", "BHAI"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ha-Arab` locale.
+pub static HA_ARAB: Locale<'static> = Locale {
+    lang: "ha-Arab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ha-Arab-SD` locale.
+pub static HA_ARAB_SD: Locale<'static> = Locale {
+    lang: "ha-Arab-SD",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ha-GH` locale.
+pub static HA_GH: Locale<'static> = Locale {
+    lang: "ha-GH",
+    dates: Locale::new_patterns("EEEE d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janairu",
+            "Faburairu",
+            "Maris",
+            "Afirilu",
+            "Mayu",
+            "Yuni",
+            "Yuli",
+            "Agusta",
+            "Satumba",
+            "Oktoba",
+            "Nuwamba",
+            "Disamba",
+        ],
+        [
+            "Jan", "Fab", "Mar", "Afi", "May", "Yun", "Yul", "Agu", "Sat", "Okt", "Nuw", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "Y", "Y", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Lahadi", "Litinin", "Talata", "Laraba", "Alhamis", "Jummaʼa", "Asabar",
+        ],
+        ["Lah", "Lit", "Tal", "Lar", "Alh", "Jum", "Asa"],
+        ["Lh", "Li", "Ta", "Lr", "Al", "Ju", "As"],
+        ["L", "L", "T", "L", "A", "J", "A"],
+    ),
+    day_periods: Locale::new_day_periods(["Safiya", "Yamma"], ["SF", "YM"], ["SF", "YM"]),
+    eras: Locale::new_eras("K.H", "BHAI"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ha-NE` locale.
+pub static HA_NE: Locale<'static> = Locale {
+    lang: "ha-NE",
+    dates: Locale::new_patterns("EEEE d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janairu",
+            "Faburairu",
+            "Maris",
+            "Afirilu",
+            "Mayu",
+            "Yuni",
+            "Yuli",
+            "Agusta",
+            "Satumba",
+            "Oktoba",
+            "Nuwamba",
+            "Disamba",
+        ],
+        [
+            "Jan", "Fab", "Mar", "Afi", "May", "Yun", "Yul", "Agu", "Sat", "Okt", "Nuw", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "Y", "Y", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Lahadi", "Litinin", "Talata", "Laraba", "Alhamis", "Jummaʼa", "Asabar",
+        ],
+        ["Lah", "Lit", "Tal", "Lar", "Alh", "Jum", "Asa"],
+        ["Lh", "Li", "Ta", "Lr", "Al", "Ju", "As"],
+        ["L", "L", "T", "L", "A", "J", "A"],
+    ),
+    day_periods: Locale::new_day_periods(["Safiya", "Yamma"], ["SF", "YM"], ["SF", "YM"]),
+    eras: Locale::new_eras("K.H", "BHAI"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `haw` locale.
+pub static HAW: Locale<'static> = Locale {
+    lang: "haw",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ianuali",
+            "Pepeluali",
+            "Malaki",
+            "ʻApelila",
+            "Mei",
+            "Iune",
+            "Iulai",
+            "ʻAukake",
+            "Kepakemapa",
+            "ʻOkakopa",
+            "Nowemapa",
+            "Kekemapa",
+        ],
+        [
+            "Ian.", "Pep.", "Mal.", "ʻAp.", "Mei", "Iun.", "Iul.", "ʻAu.", "Kep.", "ʻOk.", "Now.",
+            "Kek.",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Lāpule",
+            "Poʻakahi",
+            "Poʻalua",
+            "Poʻakolu",
+            "Poʻahā",
+            "Poʻalima",
+            "Poʻaono",
+        ],
+        ["LP", "P1", "P2", "P3", "P4", "P5", "P6"],
+        ["LP", "P1", "P2", "P3", "P4", "P5", "P6"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `he` locale.
+pub static HE: Locale<'static> = Locale {
+    lang: "he",
+    dates: Locale::new_patterns("EEEE, d בMMMM y", "d בMMMM y", "d בMMM y", "d.M.y"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ינואר",
+            "פברואר",
+            "מרץ",
+            "אפריל",
+            "מאי",
+            "יוני",
+            "יולי",
+            "אוגוסט",
+            "ספטמבר",
+            "אוקטובר",
+            "נובמבר",
+            "דצמבר",
+        ],
+        [
+            "ינו׳", "פבר׳", "מרץ", "אפר׳", "מאי", "יוני", "יולי", "אוג׳", "ספט׳", "אוק׳", "נוב׳",
+            "דצמ׳",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "יום ראשון",
+            "יום שני",
+            "יום שלישי",
+            "יום רביעי",
+            "יום חמישי",
+            "יום שישי",
+            "יום שבת",
+        ],
+        [
+            "יום א׳",
+            "יום ב׳",
+            "יום ג׳",
+            "יום ד׳",
+            "יום ה׳",
+            "יום ו׳",
+            "שבת",
+        ],
+        ["א׳", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳"],
+        ["א׳", "ב׳", "ג׳", "ד׳", "ה׳", "ו׳", "ש׳"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("לפנה״ס", "לספירה"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `hi` locale.
+pub static HI: Locale<'static> = Locale {
+    lang: "hi",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फ़रवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितंबर",
+            "अक्टूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जन॰",
+            "फ़र॰",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुल॰",
+            "अग॰",
+            "सित॰",
+            "अक्टू॰",
+            "नव॰",
+            "दिस॰",
+        ],
+        [
+            "ज", "फ़", "मा", "अ", "म", "जू", "जु", "अ", "सि", "अ", "न", "दि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रविवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "गुरुवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        ["रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["am", "pm"]),
+    eras: Locale::new_eras("ईसा-पूर्व", "ईस्वी"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `hi-Latn` locale.
+pub static HI_LATN: Locale<'static> = Locale {
+    lang: "hi-Latn",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM, y", "dd MMM, y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "January",
+            "February",
+            "March",
+            "April",
+            "May",
+            "June",
+            "July",
+            "August",
+            "September",
+            "October",
+            "November",
+            "December",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Raviwaar",
+            "Somwaar",
+            "Mangalwaar",
+            "Budhwaar",
+            "Guruwaar",
+            "Shukrawaar",
+            "Shaniwaar",
+        ],
+        ["Ravi", "Som", "Mangal", "Budh", "Guru", "Shukra", "Shani"],
+        ["Ra", "So", "Ma", "Bu", "Gu", "Shu", "Sha"],
+        ["Ra", "So", "Ma", "Bu", "Gu", "Sh", "Sha"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `hnj` locale.
+pub static HNJ: Locale<'static> = Locale {
+    lang: "hnj",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞄆𞄬",
+            "𞄛𞄨𞄱𞄄𞄤𞄲𞄨",
+            "𞄒𞄫𞄰𞄒𞄪𞄱",
+            "𞄤𞄨𞄱",
+            "𞄀𞄪𞄴",
+            "𞄛𞄤𞄱𞄞𞄤𞄦",
+            "𞄔𞄩𞄴𞄆𞄨𞄰",
+            "𞄕𞄩𞄲𞄔𞄄𞄰𞄤",
+            "𞄛𞄤𞄱𞄒𞄤𞄰",
+            "𞄪𞄱𞄀𞄤𞄴",
+            "𞄚𞄦𞄲𞄤𞄚𞄄𞄰𞄫",
+            "𞄒𞄩𞄱𞄔𞄬𞄴",
+        ],
+        [
+            "𞄆𞄬",
+            "𞄛𞄨𞄱𞄄𞄤𞄲𞄨",
+            "𞄒𞄫𞄰𞄒𞄪𞄱",
+            "𞄤𞄨𞄱",
+            "𞄀𞄪𞄴",
+            "𞄛𞄤𞄱𞄞𞄤𞄦",
+            "𞄔𞄩𞄴𞄆𞄨𞄰",
+            "𞄕𞄩𞄲𞄔𞄄𞄰𞄤",
+            "𞄛𞄤𞄱𞄒𞄤𞄰",
+            "𞄪𞄱𞄀𞄤𞄴",
+            "𞄚𞄦𞄲𞄤𞄚𞄄𞄰𞄫",
+            "𞄒𞄩𞄱𞄔𞄬𞄴",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("𞄜𞄆𞄪", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `hnj-Hmnp` locale.
+pub static HNJ_HMNP: Locale<'static> = Locale {
+    lang: "hnj-Hmnp",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𞄆𞄬",
+            "𞄛𞄨𞄱𞄄𞄤𞄲𞄨",
+            "𞄒𞄫𞄰𞄒𞄪𞄱",
+            "𞄤𞄨𞄱",
+            "𞄀𞄪𞄴",
+            "𞄛𞄤𞄱𞄞𞄤𞄦",
+            "𞄔𞄩𞄴𞄆𞄨𞄰",
+            "𞄕𞄩𞄲𞄔𞄄𞄰𞄤",
+            "𞄛𞄤𞄱𞄒𞄤𞄰",
+            "𞄪𞄱𞄀𞄤𞄴",
+            "𞄚𞄦𞄲𞄤𞄚𞄄𞄰𞄫",
+            "𞄒𞄩𞄱𞄔𞄬𞄴",
+        ],
+        [
+            "𞄆𞄬",
+            "𞄛𞄨𞄱𞄄𞄤𞄲𞄨",
+            "𞄒𞄫𞄰𞄒𞄪𞄱",
+            "𞄤𞄨𞄱",
+            "𞄀𞄪𞄴",
+            "𞄛𞄤𞄱𞄞𞄤𞄦",
+            "𞄔𞄩𞄴𞄆𞄨𞄰",
+            "𞄕𞄩𞄲𞄔𞄄𞄰𞄤",
+            "𞄛𞄤𞄱𞄒𞄤𞄰",
+            "𞄪𞄱𞄀𞄤𞄴",
+            "𞄚𞄦𞄲𞄤𞄚𞄄𞄰𞄫",
+            "𞄒𞄩𞄱𞄔𞄬𞄴",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("𞄜𞄆𞄪", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `hr` locale.
+pub static HR: Locale<'static> = Locale {
+    lang: "hr",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. MMM y.", "dd. MM. y."),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siječnja",
+            "veljače",
+            "ožujka",
+            "travnja",
+            "svibnja",
+            "lipnja",
+            "srpnja",
+            "kolovoza",
+            "rujna",
+            "listopada",
+            "studenoga",
+            "prosinca",
+        ],
+        [
+            "sij", "velj", "ožu", "tra", "svi", "lip", "srp", "kol", "ruj", "lis", "stu", "pro",
+        ],
+        [
+            "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10.", "11.", "12.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedjeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["N", "P", "U", "S", "Č", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("pr. Kr.", "po. Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `hr-BA` locale.
+pub static HR_BA: Locale<'static> = Locale {
+    lang: "hr-BA",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. MMM y.", "d. M. yy."),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "siječnja",
+            "veljače",
+            "ožujka",
+            "travnja",
+            "svibnja",
+            "lipnja",
+            "srpnja",
+            "kolovoza",
+            "rujna",
+            "listopada",
+            "studenoga",
+            "prosinca",
+        ],
+        [
+            "sij", "velj", "ožu", "tra", "svi", "lip", "srp", "kol", "ruj", "lis", "stu", "pro",
+        ],
+        [
+            "1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10.", "11.", "12.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedjeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["N", "P", "U", "S", "Č", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("pr. Kr.", "po. Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `hsb` locale.
+pub static HSB: Locale<'static> = Locale {
+    lang: "hsb",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d.M.y", "d.M.yy"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm 'hodź'."),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januara",
+            "februara",
+            "měrca",
+            "apryla",
+            "meje",
+            "junija",
+            "julija",
+            "awgusta",
+            "septembra",
+            "oktobra",
+            "nowembra",
+            "decembra",
+        ],
+        [
+            "jan.", "feb.", "měr.", "apr.", "mej.", "jun.", "jul.", "awg.", "sep.", "okt.", "now.",
+            "dec.",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "njedźela",
+            "póndźela",
+            "wutora",
+            "srjeda",
+            "štwórtk",
+            "pjatk",
+            "sobota",
+        ],
+        ["nje", "pón", "wut", "srj", "štw", "pja", "sob"],
+        ["nj", "pó", "wu", "sr", "št", "pj", "so"],
+        ["n", "p", "w", "s", "š", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["dopołdnja", "popołdnju"],
+        ["dop.", "pop."],
+        ["dop.", "pop."],
+    ),
+    eras: Locale::new_eras("př.Chr.n.", "po Chr.n."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ht` locale.
+pub static HT: Locale<'static> = Locale {
+    lang: "ht",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvier",
+            "février",
+            "mars",
+            "avril",
+            "mai",
+            "juin",
+            "juillet",
+            "août",
+            "septembre",
+            "octobre",
+            "novembre",
+            "décembre",
+        ],
+        [
+            "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.",
+            "nov.", "déc.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi",
+        ],
+        ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
+        ["di", "lu", "ma", "me", "je", "ve", "sa"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. J.-C.", "ap. J.-C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `hu` locale.
+pub static HU: Locale<'static> = Locale {
+    lang: "hu",
+    dates: Locale::new_patterns("y. MMMM d., EEEE", "y. MMMM d.", "y. MMM d.", "y. MM. dd."),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "január",
+            "február",
+            "március",
+            "április",
+            "május",
+            "június",
+            "július",
+            "augusztus",
+            "szeptember",
+            "október",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "febr.", "márc.", "ápr.", "máj.", "jún.", "júl.", "aug.", "szept.", "okt.",
+            "nov.", "dec.",
+        ],
+        ["J", "F", "M", "Á", "M", "J", "J", "A", "Sz", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "vasárnap",
+            "hétfő",
+            "kedd",
+            "szerda",
+            "csütörtök",
+            "péntek",
+            "szombat",
+        ],
+        ["V", "H", "K", "Sze", "Cs", "P", "Szo"],
+        ["V", "H", "K", "Sze", "Cs", "P", "Szo"],
+        ["V", "H", "K", "Sz", "Cs", "P", "Sz"],
+    ),
+    day_periods: Locale::new_day_periods(["de.", "du."], ["de.", "du."], ["de.", "du."]),
+    eras: Locale::new_eras("i. e.", "i. sz."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `hy` locale.
+pub static HY: Locale<'static> = Locale {
+    lang: "hy",
+    dates: Locale::new_patterns(
+        "y թ. MMMM d, EEEE",
+        "dd MMMM, y թ.",
+        "dd MMM, y թ.",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "հունվարի",
+            "փետրվարի",
+            "մարտի",
+            "ապրիլի",
+            "մայիսի",
+            "հունիսի",
+            "հուլիսի",
+            "օգոստոսի",
+            "սեպտեմբերի",
+            "հոկտեմբերի",
+            "նոյեմբերի",
+            "դեկտեմբերի",
+        ],
+        [
+            "հնվ", "փտվ", "մրտ", "ապր", "մյս", "հնս", "հլս", "օգս", "սեպ", "հոկ", "նոյ", "դեկ",
+        ],
+        ["Հ", "Փ", "Մ", "Ա", "Մ", "Հ", "Հ", "Օ", "Ս", "Հ", "Ն", "Դ"],
+    ),
+    days: Locale::new_days(
+        [
+            "կիրակի",
+            "երկուշաբթի",
+            "երեքշաբթի",
+            "չորեքշաբթի",
+            "հինգշաբթի",
+            "ուրբաթ",
+            "շաբաթ",
+        ],
+        ["կիր", "երկ", "երք", "չրք", "հնգ", "ուր", "շբթ"],
+        ["կր", "եկ", "եք", "չք", "հգ", "ու", "շբ"],
+        ["Կ", "Ե", "Ե", "Չ", "Հ", "Ո", "Շ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["ա", "հ"]),
+    eras: Locale::new_eras("մ.թ.ա.", "մ.թ."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ia` locale.
+pub static IA: Locale<'static> = Locale {
+    lang: "ia",
+    dates: Locale::new_patterns(
+        "EEEE 'le' d 'de' MMMM y",
+        "d 'de' MMMM y",
+        "d MMM y",
+        "dd-MM-y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januario",
+            "februario",
+            "martio",
+            "april",
+            "maio",
+            "junio",
+            "julio",
+            "augusto",
+            "septembre",
+            "octobre",
+            "novembre",
+            "decembre",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "mai", "jun", "jul", "aug", "sep", "oct", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "dominica",
+            "lunedi",
+            "martedi",
+            "mercuridi",
+            "jovedi",
+            "venerdi",
+            "sabbato",
+        ],
+        ["dom", "lun", "mar", "mer", "jov", "ven", "sab"],
+        ["do", "lu", "ma", "me", "jo", "ve", "sa"],
+        ["d", "l", "m", "m", "j", "v", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("a.Chr.", "p.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `id` locale.
+pub static ID: Locale<'static> = Locale {
+    lang: "id",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Maret",
+            "April",
+            "Mei",
+            "Juni",
+            "Juli",
+            "Agustus",
+            "September",
+            "Oktober",
+            "November",
+            "Desember",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agu", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu",
+        ],
+        ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+        ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+        ["M", "S", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("SM", "M"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ie` locale.
+pub static IE: Locale<'static> = Locale {
+    lang: "ie",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d.M.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "marte",
+            "april",
+            "may",
+            "junio",
+            "julí",
+            "august",
+            "septembre",
+            "octobre",
+            "novembre",
+            "decembre",
+        ],
+        [
+            "jan.", "febr.", "mar.", "apr.", "may", "jun.", "julí", "aug.", "sept.", "oct.",
+            "nov.", "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "soledí",
+            "lunedí",
+            "mardí",
+            "mercurdí",
+            "jovedí",
+            "venerdí",
+            "saturdí",
+        ],
+        ["sol.", "lun.", "mar.", "mer.", "jov.", "ven.", "sat."],
+        ["So", "Lu", "Ma", "Me", "Jo", "Ve", "Sa"],
+        ["S", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ante midí", "pos midí"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "e.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ig` locale.
+pub static IG: Locale<'static> = Locale {
+    lang: "ig",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Jenụwarị",
+            "Febrụwarị",
+            "Maachị",
+            "Epreel",
+            "Mee",
+            "Jun",
+            "Julaị",
+            "Ọgọọst",
+            "Septemba",
+            "Ọktoba",
+            "Novemba",
+            "Disemba",
+        ],
+        [
+            "Jen", "Feb", "Maa", "Epr", "Mee", "Juu", "Jul", "Ọgọ", "Sep", "Ọkt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "E", "M", "J", "J", "Ọ", "S", "Ọ", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sọndee",
+            "Mọnde",
+            "Tiuzdee",
+            "Wenezdee",
+            "Tọọzdee",
+            "Fraịdee",
+            "Satọdee",
+        ],
+        ["Sọn", "Mọn", "Tiu", "Wen", "Tọọ", "Fraị", "Sat"],
+        ["Sọn", "Mọn", "Tiu", "Wen", "Tọọ", "Fraị", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["N’ụtụtụ", "N’abali"],
+        ["N’ụtụtụ", "N’abalị"],
+        ["N’ụtụtụ", "N’abalị"],
+    ),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ii` locale.
+pub static II: Locale<'static> = Locale {
+    lang: "ii",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ꋍꆪ",
+            "ꑍꆪ",
+            "ꌕꆪ",
+            "ꇖꆪ",
+            "ꉬꆪ",
+            "ꃘꆪ",
+            "ꏃꆪ",
+            "ꉆꆪ",
+            "ꈬꆪ",
+            "ꊰꆪ",
+            "ꊯꊪꆪ",
+            "ꊰꑋꆪ",
+        ],
+        [
+            "ꋍꆪ",
+            "ꑍꆪ",
+            "ꌕꆪ",
+            "ꇖꆪ",
+            "ꉬꆪ",
+            "ꃘꆪ",
+            "ꏃꆪ",
+            "ꉆꆪ",
+            "ꈬꆪ",
+            "ꊰꆪ",
+            "ꊯꊪꆪ",
+            "ꊰꑋꆪ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ꑬꆏꑍ",
+            "ꆏꊂꋍ",
+            "ꆏꊂꑍ",
+            "ꆏꊂꌕ",
+            "ꆏꊂꇖ",
+            "ꆏꊂꉬ",
+            "ꆏꊂꃘ",
+        ],
+        ["ꑬꆏ", "ꆏꋍ", "ꆏꑍ", "ꆏꌕ", "ꆏꇖ", "ꆏꉬ", "ꆏꃘ"],
+        ["ꑬꆏ", "ꆏꋍ", "ꆏꑍ", "ꆏꌕ", "ꆏꇖ", "ꆏꉬ", "ꆏꃘ"],
+        ["ꆏ", "ꋍ", "ꑍ", "ꌕ", "ꇖ", "ꉬ", "ꃘ"],
+    ),
+    day_periods: Locale::new_day_periods(["ꎸꄑ", "ꁯꋒ"], ["ꎸꄑ", "ꁯꋒ"], ["ꎸꄑ", "ꁯꋒ"]),
+    eras: Locale::new_eras("ꃅꋊꂿ", "ꃅꋊꊂ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `io` locale.
+pub static IO: Locale<'static> = Locale {
+    lang: "io",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `is` locale.
+pub static IS: Locale<'static> = Locale {
+    lang: "is",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d. MMM y", "d.M.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janúar",
+            "febrúar",
+            "mars",
+            "apríl",
+            "maí",
+            "júní",
+            "júlí",
+            "ágúst",
+            "september",
+            "október",
+            "nóvember",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "maí", "jún.", "júl.", "ágú.", "sep.", "okt.", "nóv.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "Á", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "sunnudagur",
+            "mánudagur",
+            "þriðjudagur",
+            "miðvikudagur",
+            "fimmtudagur",
+            "föstudagur",
+            "laugardagur",
+        ],
+        ["sun.", "mán.", "þri.", "mið.", "fim.", "fös.", "lau."],
+        ["su.", "má.", "þr.", "mi.", "fi.", "fö.", "la."],
+        ["S", "M", "Þ", "M", "F", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["f.h.", "e.h."], ["f.h.", "e.h."], ["f.", "e."]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `it` locale.
+pub static IT: Locale<'static> = Locale {
+    lang: "it",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "gennaio",
+            "febbraio",
+            "marzo",
+            "aprile",
+            "maggio",
+            "giugno",
+            "luglio",
+            "agosto",
+            "settembre",
+            "ottobre",
+            "novembre",
+            "dicembre",
+        ],
+        [
+            "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic",
+        ],
+        ["G", "F", "M", "A", "M", "G", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domenica",
+            "lunedì",
+            "martedì",
+            "mercoledì",
+            "giovedì",
+            "venerdì",
+            "sabato",
+        ],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["D", "L", "M", "M", "G", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["m.", "p."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `it-CH` locale.
+pub static IT_CH: Locale<'static> = Locale {
+    lang: "it-CH",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "gennaio",
+            "febbraio",
+            "marzo",
+            "aprile",
+            "maggio",
+            "giugno",
+            "luglio",
+            "agosto",
+            "settembre",
+            "ottobre",
+            "novembre",
+            "dicembre",
+        ],
+        [
+            "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic",
+        ],
+        ["G", "F", "M", "A", "M", "G", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domenica",
+            "lunedì",
+            "martedì",
+            "mercoledì",
+            "giovedì",
+            "venerdì",
+            "sabato",
+        ],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["D", "L", "M", "M", "G", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["m.", "p."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `it-SM` locale.
+pub static IT_SM: Locale<'static> = Locale {
+    lang: "it-SM",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "gennaio",
+            "febbraio",
+            "marzo",
+            "aprile",
+            "maggio",
+            "giugno",
+            "luglio",
+            "agosto",
+            "settembre",
+            "ottobre",
+            "novembre",
+            "dicembre",
+        ],
+        [
+            "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic",
+        ],
+        ["G", "F", "M", "A", "M", "G", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domenica",
+            "lunedì",
+            "martedì",
+            "mercoledì",
+            "giovedì",
+            "venerdì",
+            "sabato",
+        ],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["D", "L", "M", "M", "G", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["m.", "p."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `it-VA` locale.
+pub static IT_VA: Locale<'static> = Locale {
+    lang: "it-VA",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "gennaio",
+            "febbraio",
+            "marzo",
+            "aprile",
+            "maggio",
+            "giugno",
+            "luglio",
+            "agosto",
+            "settembre",
+            "ottobre",
+            "novembre",
+            "dicembre",
+        ],
+        [
+            "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic",
+        ],
+        ["G", "F", "M", "A", "M", "G", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domenica",
+            "lunedì",
+            "martedì",
+            "mercoledì",
+            "giovedì",
+            "venerdì",
+            "sabato",
+        ],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["dom", "lun", "mar", "mer", "gio", "ven", "sab"],
+        ["D", "L", "M", "M", "G", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["m.", "p."]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `iu` locale.
+pub static IU: Locale<'static> = Locale {
+    lang: "iu",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "MM/dd/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `iu-Latn` locale.
+pub static IU_LATN: Locale<'static> = Locale {
+    lang: "iu-Latn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ja` locale.
+pub static JA: Locale<'static> = Locale {
+    lang: "ja",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y/MM/dd", "y/MM/dd"),
+    times: Locale::new_patterns("H時mm分ss秒 zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "日曜日",
+            "月曜日",
+            "火曜日",
+            "水曜日",
+            "木曜日",
+            "金曜日",
+            "土曜日",
+        ],
+        ["日", "月", "火", "水", "木", "金", "土"],
+        ["日", "月", "火", "水", "木", "金", "土"],
+        ["日", "月", "火", "水", "木", "金", "土"],
+    ),
+    day_periods: Locale::new_day_periods(["午前", "午後"], ["午前", "午後"], ["午前", "午後"]),
+    eras: Locale::new_eras("紀元前", "西暦"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `jbo` locale.
+pub static JBO: Locale<'static> = Locale {
+    lang: "jbo",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `jgo` locale.
+pub static JGO: Locale<'static> = Locale {
+    lang: "jgo",
+    dates: Locale::new_patterns("EEEE, y MMMM dd", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Nduŋmbi Saŋ",
+            "Pɛsaŋ Pɛ́pá",
+            "Pɛsaŋ Pɛ́tát",
+            "Pɛsaŋ Pɛ́nɛ́kwa",
+            "Pɛsaŋ Pataa",
+            "Pɛsaŋ Pɛ́nɛ́ntúkú",
+            "Pɛsaŋ Saambá",
+            "Pɛsaŋ Pɛ́nɛ́fɔm",
+            "Pɛsaŋ Pɛ́nɛ́pfúꞋú",
+            "Pɛsaŋ Nɛgɛ́m",
+            "Pɛsaŋ Ntsɔ̌pmɔ́",
+            "Pɛsaŋ Ntsɔ̌ppá",
+        ],
+        [
+            "Nduŋmbi Saŋ",
+            "Pɛsaŋ Pɛ́pá",
+            "Pɛsaŋ Pɛ́tát",
+            "Pɛsaŋ Pɛ́nɛ́kwa",
+            "Pɛsaŋ Pataa",
+            "Pɛsaŋ Pɛ́nɛ́ntúkú",
+            "Pɛsaŋ Saambá",
+            "Pɛsaŋ Pɛ́nɛ́fɔm",
+            "Pɛsaŋ Pɛ́nɛ́pfúꞋú",
+            "Pɛsaŋ Nɛgɛ́m",
+            "Pɛsaŋ Ntsɔ̌pmɔ́",
+            "Pɛsaŋ Ntsɔ̌ppá",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sɔ́ndi",
+            "Mɔ́ndi",
+            "Ápta Mɔ́ndi",
+            "Wɛ́nɛsɛdɛ",
+            "Tɔ́sɛdɛ",
+            "Fɛlâyɛdɛ",
+            "Sásidɛ",
+        ],
+        [
+            "Sɔ́ndi",
+            "Mɔ́ndi",
+            "Ápta Mɔ́ndi",
+            "Wɛ́nɛsɛdɛ",
+            "Tɔ́sɛdɛ",
+            "Fɛlâyɛdɛ",
+            "Sásidɛ",
+        ],
+        [
+            "Sɔ́ndi",
+            "Mɔ́ndi",
+            "Ápta Mɔ́ndi",
+            "Wɛ́nɛsɛdɛ",
+            "Tɔ́sɛdɛ",
+            "Fɛlâyɛdɛ",
+            "Sásidɛ",
+        ],
+        ["Sɔ́", "Mɔ́", "ÁM", "Wɛ́", "Tɔ́", "Fɛ", "Sá"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["mbaꞌmbaꞌ", "ŋka mbɔ́t nji"],
+        ["mbaꞌmbaꞌ", "ŋka mbɔ́t nji"],
+        ["mbaꞌmbaꞌ", "ŋka mbɔ́t nji"],
+    ),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `jmc` locale.
+pub static JMC: Locale<'static> = Locale {
+    lang: "jmc",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprilyi", "Mei", "Junyi", "Julyai", "Agusti",
+            "Septemba", "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapilyi",
+            "Jumatatuu",
+            "Jumanne",
+            "Jumatanu",
+            "Alhamisi",
+            "Ijumaa",
+            "Jumamosi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["J", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `jv` locale.
+pub static JV: Locale<'static> = Locale {
+    lang: "jv",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Maret",
+            "April",
+            "Mei",
+            "Juni",
+            "Juli",
+            "Agustus",
+            "September",
+            "Oktober",
+            "November",
+            "Desember",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agt", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["Ahad", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"],
+        ["Ahad", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+        ["Ahad", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"],
+        ["A", "S", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["Isuk", "Wengi"], ["Isuk", "Wengi"], ["Isuk", "Wengi"]),
+    eras: Locale::new_eras("SM", "M"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ka` locale.
+pub static KA: Locale<'static> = Locale {
+    lang: "ka",
+    dates: Locale::new_patterns("EEEE, dd MMMM, y", "d MMMM, y", "d MMM. y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "იანვარი",
+            "თებერვალი",
+            "მარტი",
+            "აპრილი",
+            "მაისი",
+            "ივნისი",
+            "ივლისი",
+            "აგვისტო",
+            "სექტემბერი",
+            "ოქტომბერი",
+            "ნოემბერი",
+            "დეკემბერი",
+        ],
+        [
+            "იან",
+            "თებ",
+            "მარ",
+            "აპრ",
+            "მაი",
+            "ივნ",
+            "ივლ",
+            "აგვ",
+            "სექ",
+            "ოქტ",
+            "ნოე",
+            "დეკ",
+        ],
+        ["ი", "თ", "მ", "ა", "მ", "ი", "ი", "ა", "ს", "ო", "ნ", "დ"],
+    ),
+    days: Locale::new_days(
+        [
+            "კვირა",
+            "ორშაბათი",
+            "სამშაბათი",
+            "ოთხშაბათი",
+            "ხუთშაბათი",
+            "პარასკევი",
+            "შაბათი",
+        ],
+        ["კვი", "ორშ", "სამ", "ოთხ", "ხუთ", "პარ", "შაბ"],
+        ["კვ", "ორ", "სმ", "ოთ", "ხთ", "პრ", "შბ"],
+        ["კ", "ო", "ს", "ო", "ხ", "პ", "შ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("ძვ. წ.", "ახ. წ."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kaa` locale.
+pub static KAA: Locale<'static> = Locale {
+    lang: "kaa",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "январь",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "екшемби",
+            "дүйшемби",
+            "сийшемби",
+            "сәршемби",
+            "пийшемби",
+            "жума",
+            "шемби",
+        ],
+        ["Екш", "Дүй", "Сий", "Сәр", "Пий", "Жум", "Шем"],
+        ["Ек", "Дү", "Си", "Сә", "Пи", "Жу", "Ше"],
+        ["Е", "Д", "С", "С", "П", "Ж", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б. э. ш.", "б. э."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kaa-Cyrl` locale.
+pub static KAA_CYRL: Locale<'static> = Locale {
+    lang: "kaa-Cyrl",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "январь",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "екшемби",
+            "дүйшемби",
+            "сийшемби",
+            "сәршемби",
+            "пийшемби",
+            "жума",
+            "шемби",
+        ],
+        ["Екш", "Дүй", "Сий", "Сәр", "Пий", "Жум", "Шем"],
+        ["Ек", "Дү", "Си", "Сә", "Пи", "Жу", "Ше"],
+        ["Е", "Д", "С", "С", "П", "Ж", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б. э. ш.", "б. э."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kaa-Latn` locale.
+pub static KAA_LATN: Locale<'static> = Locale {
+    lang: "kaa-Latn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kab` locale.
+pub static KAB: Locale<'static> = Locale {
+    lang: "kab",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Yennayer",
+            "Fuṛar",
+            "Meɣres",
+            "Yebrir",
+            "Mayyu",
+            "Yunyu",
+            "Yulyu",
+            "Ɣuct",
+            "Ctembeṛ",
+            "Tubeṛ",
+            "Nunembeṛ",
+            "Duǧembeṛ",
+        ],
+        [
+            "Yen", "Fur", "Meɣ", "Yeb", "May", "Yun", "Yul", "Ɣuc", "Cte", "Tub", "Nun", "Duǧ",
+        ],
+        ["Y", "F", "M", "Y", "M", "Y", "Y", "Ɣ", "C", "T", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["Acer", "Arim", "Aram", "Ahad", "Amhad", "Sem", "Sed"],
+        ["Acer", "Arim", "Aram", "Ahad", "Amhad", "Sem", "Sed"],
+        ["Acer", "Arim", "Aram", "Ahad", "Amhad", "Sem", "Sed"],
+        ["C", "R", "R", "H", "M", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["n tufat", "n tmeddit"],
+        ["n tufat", "n tmeddit"],
+        ["n tufat", "n tmeddit"],
+    ),
+    eras: Locale::new_eras("snd. T.Ɛ", "sld. T.Ɛ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kaj` locale.
+pub static KAJ: Locale<'static> = Locale {
+    lang: "kaj",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kam` locale.
+pub static KAM: Locale<'static> = Locale {
+    lang: "kam",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mwai wa mbee",
+            "Mwai wa kelĩ",
+            "Mwai wa katatũ",
+            "Mwai wa kana",
+            "Mwai wa katano",
+            "Mwai wa thanthatũ",
+            "Mwai wa muonza",
+            "Mwai wa nyaanya",
+            "Mwai wa kenda",
+            "Mwai wa ĩkumi",
+            "Mwai wa ĩkumi na ĩmwe",
+            "Mwai wa ĩkumi na ilĩ",
+        ],
+        [
+            "Mbe", "Kel", "Ktũ", "Kan", "Ktn", "Tha", "Moo", "Nya", "Knd", "Ĩku", "Ĩkm", "Ĩkl",
+        ],
+        ["M", "K", "K", "K", "K", "T", "M", "N", "K", "Ĩ", "Ĩ", "Ĩ"],
+    ),
+    days: Locale::new_days(
+        [
+            "Wa kyumwa",
+            "Wa kwambĩlĩlya",
+            "Wa kelĩ",
+            "Wa katatũ",
+            "Wa kana",
+            "Wa katano",
+            "Wa thanthatũ",
+        ],
+        ["Wky", "Wkw", "Wkl", "Wtũ", "Wkn", "Wtn", "Wth"],
+        ["Wky", "Wkw", "Wkl", "Wtũ", "Wkn", "Wtn", "Wth"],
+        ["Y", "W", "E", "A", "A", "A", "A"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Ĩyakwakya", "Ĩyawĩoo"],
+        ["Ĩyakwakya", "Ĩyawĩoo"],
+        ["Ĩyakwakya", "Ĩyawĩoo"],
+    ),
+    eras: Locale::new_eras("MY", "IY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kcg` locale.
+pub static KCG: Locale<'static> = Locale {
+    lang: "kcg",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kde` locale.
+pub static KDE: Locale<'static> = Locale {
+    lang: "kde",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mwedi Ntandi",
+            "Mwedi wa Pili",
+            "Mwedi wa Tatu",
+            "Mwedi wa Nchechi",
+            "Mwedi wa Nnyano",
+            "Mwedi wa Nnyano na Umo",
+            "Mwedi wa Nnyano na Mivili",
+            "Mwedi wa Nnyano na Mitatu",
+            "Mwedi wa Nnyano na Nchechi",
+            "Mwedi wa Nnyano na Nnyano",
+            "Mwedi wa Nnyano na Nnyano na U",
+            "Mwedi wa Nnyano na Nnyano na M",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Liduva lyapili",
+            "Liduva lyatatu",
+            "Liduva lyanchechi",
+            "Liduva lyannyano",
+            "Liduva lyannyano na linji",
+            "Liduva lyannyano na mavili",
+            "Liduva litandi",
+        ],
+        ["Ll2", "Ll3", "Ll4", "Ll5", "Ll6", "Ll7", "Ll1"],
+        ["Ll2", "Ll3", "Ll4", "Ll5", "Ll6", "Ll7", "Ll1"],
+        ["2", "3", "4", "5", "6", "7", "1"],
+    ),
+    day_periods: Locale::new_day_periods(["Muhi", "Chilo"], ["Muhi", "Chilo"], ["Muhi", "Chilo"]),
+    eras: Locale::new_eras("AY", "NY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kea` locale.
+pub static KEA: Locale<'static> = Locale {
+    lang: "kea",
+    dates: Locale::new_patterns(
+        "EEEE, d 'di' MMMM 'di' y",
+        "d 'di' MMMM 'di' y",
+        "d MMM y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janeru", "Febreru", "Marsu", "Abril", "Maiu", "Junhu", "Julhu", "Agostu", "Setenbru",
+            "Otubru", "Nuvenbru", "Dizenbru",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Otu", "Nuv", "Diz",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dumingu",
+            "sigunda-fera",
+            "tersa-fera",
+            "kuarta-fera",
+            "kinta-fera",
+            "sesta-fera",
+            "sábadu",
+        ],
+        ["dum", "sig", "ter", "kua", "kin", "ses", "sab"],
+        ["du", "si", "te", "ku", "ki", "se", "sa"],
+        ["D", "S", "T", "K", "K", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["am", "pm"]),
+    eras: Locale::new_eras("AK", "DK"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kek` locale.
+pub static KEK: Locale<'static> = Locale {
+    lang: "kek",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Xbʼeen Po",
+            "Xkabʼ Po",
+            "Rox Po",
+            "Xkaa Po",
+            "Roʼ Po",
+            "Xwaq Po",
+            "Xwuuq Po",
+            "Xwaqxaq Po",
+            "Xbʼelee Po",
+            "Xlajee Po",
+            "Xjunlaj Po",
+            "Xkabʼlaj Po",
+        ],
+        [
+            "Xbʼeen Po",
+            "Xkabʼ Po",
+            "Rox Po",
+            "Xkaa Po",
+            "Roʼ Po",
+            "Xwaq Po",
+            "Xwuuq Po",
+            "Xwaqxaq Po",
+            "Xbʼelee Po",
+            "Xlajee Po",
+            "Xjunlaj Po",
+            "Xkabʼlaj Po",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Doʼkutan",
+            "LuʼKutan",
+            "MarʼKutan",
+            "MerʼKutan",
+            "JueʼKutan",
+            "VierʼKutan",
+            "SabʼKutan",
+        ],
+        [
+            "Doʼkutan",
+            "LuʼKutan",
+            "MarʼKutan",
+            "MerʼKutan",
+            "JueʼKutan",
+            "VierʼKutan",
+            "SabʼKutan",
+        ],
+        [
+            "Doʼkutan",
+            "LuʼKutan",
+            "MarʼKutan",
+            "MerʼKutan",
+            "JueʼKutan",
+            "VierʼKutan",
+            "SabʼKutan",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["Eqʼela", "Ewu"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ken` locale.
+pub static KEN: Locale<'static> = Locale {
+    lang: "ken",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kgp` locale.
+pub static KGP: Locale<'static> = Locale {
+    lang: "kgp",
+    dates: Locale::new_patterns(
+        "EEEE, d 'ne' MMMM, y",
+        "d 'ne' MMMM, y",
+        "d 'ne' MMM, y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1-Kysã", "2-Kysã", "3-Kysã", "4-Kysã", "5-Kysã", "6-Kysã", "7-Kysã", "8-Kysã",
+            "9-Kysã", "10-Kysã", "11-Kysã", "12-Kysã",
+        ],
+        [
+            "1Ky.", "2Ky.", "3Ky.", "4Ky.", "5Ky.", "6Ky.", "7Ky.", "8Ky.", "9Ky.", "10Ky.",
+            "11Ky.", "12Ky.",
+        ],
+        [
+            "1K", "2K", "3K", "4K", "5K", "6K", "7K", "8K", "9K", "10K", "11K", "12K",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "numĩggu",
+            "pir-kurã-há",
+            "régre-kurã-há",
+            "tẽgtũ-kurã-há",
+            "vẽnhkãgra-kurã-há",
+            "pénkar-kurã-há",
+            "savnu",
+        ],
+        ["num.", "pir.", "rég.", "tẽg.", "vẽn.", "pén.", "sav."],
+        ["N.", "1kh.", "2kh.", "3kh.", "4kh.", "5kh.", "S."],
+        ["N.", "P.", "R.", "T.", "V.", "P.", "S."],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("C.j.", "C.kk."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `khq` locale.
+pub static KHQ: Locale<'static> = Locale {
+    lang: "khq",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Žanwiye",
+            "Feewiriye",
+            "Marsi",
+            "Awiril",
+            "Me",
+            "Žuweŋ",
+            "Žuyye",
+            "Ut",
+            "Sektanbur",
+            "Oktoobur",
+            "Noowanbur",
+            "Deesanbur",
+        ],
+        [
+            "Žan", "Fee", "Mar", "Awi", "Me", "Žuw", "Žuy", "Ut", "Sek", "Okt", "Noo", "Dee",
+        ],
+        ["Ž", "F", "M", "A", "M", "Ž", "Ž", "U", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Alhadi",
+            "Atini",
+            "Atalata",
+            "Alarba",
+            "Alhamiisa",
+            "Aljuma",
+            "Assabdu",
+        ],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alj", "Ass"],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alj", "Ass"],
+        ["H", "T", "T", "L", "L", "L", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Adduha", "Aluula"],
+        ["Adduha", "Aluula"],
+        ["Adduha", "Aluula"],
+    ),
+    eras: Locale::new_eras("IJ", "IZ"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `ki` locale.
+pub static KI: Locale<'static> = Locale {
+    lang: "ki",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Njenuarĩ",
+            "Mwere wa kerĩ",
+            "Mwere wa gatatũ",
+            "Mwere wa kana",
+            "Mwere wa gatano",
+            "Mwere wa gatandatũ",
+            "Mwere wa mũgwanja",
+            "Mwere wa kanana",
+            "Mwere wa kenda",
+            "Mwere wa ikũmi",
+            "Mwere wa ikũmi na ũmwe",
+            "Ndithemba",
+        ],
+        [
+            "JEN", "WKR", "WGT", "WKN", "WTN", "WTD", "WMJ", "WNN", "WKD", "WIK", "WMW", "DIT",
+        ],
+        ["J", "K", "G", "K", "G", "G", "M", "K", "K", "I", "I", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Kiumia",
+            "Njumatatũ",
+            "Njumaine",
+            "Njumatana",
+            "Aramithi",
+            "Njumaa",
+            "Njumamothi",
+        ],
+        ["KMA", "NTT", "NMN", "NMT", "ART", "NMA", "NMM"],
+        ["KMA", "NTT", "NMN", "NMT", "ART", "NMA", "NMM"],
+        ["K", "N", "N", "N", "A", "N", "N"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Kiroko", "Hwaĩ-inĩ"],
+        ["Kiroko", "Hwaĩ-inĩ"],
+        ["Kiroko", "Hwaĩ-inĩ"],
+    ),
+    eras: Locale::new_eras("MK", "TK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kk` locale.
+pub static KK: Locale<'static> = Locale {
+    lang: "kk",
+    dates: Locale::new_patterns(
+        "y 'ж'. d MMMM, EEEE",
+        "y 'ж'. d MMMM",
+        "y 'ж'. dd MMM",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "қаңтар",
+            "ақпан",
+            "наурыз",
+            "сәуір",
+            "мамыр",
+            "маусым",
+            "шілде",
+            "тамыз",
+            "қыркүйек",
+            "қазан",
+            "қараша",
+            "желтоқсан",
+        ],
+        [
+            "қаң.", "ақп.", "нау.", "сәу.", "мам.", "мау.", "шіл.", "там.", "қыр.", "қаз.", "қар.",
+            "жел.",
+        ],
+        ["Қ", "А", "Н", "С", "М", "М", "Ш", "Т", "Қ", "Қ", "Қ", "Ж"],
+    ),
+    days: Locale::new_days(
+        [
+            "жексенбі",
+            "дүйсенбі",
+            "сейсенбі",
+            "сәрсенбі",
+            "бейсенбі",
+            "жұма",
+            "сенбі",
+        ],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["Ж", "Д", "С", "С", "Б", "Ж", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б.з.д.", "б.з."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kk-Arab` locale.
+pub static KK_ARAB: Locale<'static> = Locale {
+    lang: "kk-Arab",
+    dates: Locale::new_patterns("y، d- MMMM، EEEE", "y، d- MMMM", "y، d- MMM", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "قاڭتار",
+            "اقپان",
+            "ناۋرىز",
+            "ءساۋىر",
+            "مامىر",
+            "ماۋسىم",
+            "شىلدە",
+            "تامىز",
+            "قىركۇيەك",
+            "قازان",
+            "قاراشا",
+            "جەلتوقسان",
+        ],
+        [
+            "قاڭتار",
+            "اقپان",
+            "ناۋرىز",
+            "ءساۋىر",
+            "مامىر",
+            "ماۋسىم",
+            "شىلدە",
+            "تامىز",
+            "قىركۇيەك",
+            "قازان",
+            "قاراشا",
+            "جەلتوقسان",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "جەكسەنبى",
+            "دۇيسەنبى",
+            "سەيسەنبى",
+            "سارسەنبى",
+            "بەيسەنبى",
+            "جۇما",
+            "سەنبى",
+        ],
+        ["جەك", "دۇي", "سەي", "سار", "بەي", "جۇم", "سەن"],
+        ["جە", "دۇ", "سە", "سا", "بە", "جۇ", "سن"],
+        ["ج", "د", "س", "س", "ب", "ج", "س"],
+    ),
+    day_periods: Locale::new_day_periods(["ت د", "ت ك"], ["ت د", "ت ك"], ["ت د", "ت ك"]),
+    eras: Locale::new_eras("ب ز د", "ب ز"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kk-Cyrl` locale.
+pub static KK_CYRL: Locale<'static> = Locale {
+    lang: "kk-Cyrl",
+    dates: Locale::new_patterns(
+        "y 'ж'. d MMMM, EEEE",
+        "y 'ж'. d MMMM",
+        "y 'ж'. dd MMM",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "қаңтар",
+            "ақпан",
+            "наурыз",
+            "сәуір",
+            "мамыр",
+            "маусым",
+            "шілде",
+            "тамыз",
+            "қыркүйек",
+            "қазан",
+            "қараша",
+            "желтоқсан",
+        ],
+        [
+            "қаң.", "ақп.", "нау.", "сәу.", "мам.", "мау.", "шіл.", "там.", "қыр.", "қаз.", "қар.",
+            "жел.",
+        ],
+        ["Қ", "А", "Н", "С", "М", "М", "Ш", "Т", "Қ", "Қ", "Қ", "Ж"],
+    ),
+    days: Locale::new_days(
+        [
+            "жексенбі",
+            "дүйсенбі",
+            "сейсенбі",
+            "сәрсенбі",
+            "бейсенбі",
+            "жұма",
+            "сенбі",
+        ],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["Ж", "Д", "С", "С", "Б", "Ж", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б.з.д.", "б.з."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kk-KZ` locale.
+pub static KK_KZ: Locale<'static> = Locale {
+    lang: "kk-KZ",
+    dates: Locale::new_patterns(
+        "y 'ж'. d MMMM, EEEE",
+        "y 'ж'. d MMMM",
+        "y 'ж'. dd MMM",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "қаңтар",
+            "ақпан",
+            "наурыз",
+            "сәуір",
+            "мамыр",
+            "маусым",
+            "шілде",
+            "тамыз",
+            "қыркүйек",
+            "қазан",
+            "қараша",
+            "желтоқсан",
+        ],
+        [
+            "қаң.", "ақп.", "нау.", "сәу.", "мам.", "мау.", "шіл.", "там.", "қыр.", "қаз.", "қар.",
+            "жел.",
+        ],
+        ["Қ", "А", "Н", "С", "М", "М", "Ш", "Т", "Қ", "Қ", "Қ", "Ж"],
+    ),
+    days: Locale::new_days(
+        [
+            "жексенбі",
+            "дүйсенбі",
+            "сейсенбі",
+            "сәрсенбі",
+            "бейсенбі",
+            "жұма",
+            "сенбі",
+        ],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["жс", "дс", "сс", "ср", "бс", "жм", "сб"],
+        ["Ж", "Д", "С", "С", "Б", "Ж", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б.з.д.", "б.з."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `kkj` locale.
+pub static KKJ: Locale<'static> = Locale {
+    lang: "kkj",
+    dates: Locale::new_patterns("EEEE dd MMMM y", "d MMMM y", "d MMM y", "dd/MM y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "pamba",
+            "wanja",
+            "mbiyɔ mɛndoŋgɔ",
+            "Nyɔlɔmbɔŋgɔ",
+            "Mɔnɔ ŋgbanja",
+            "Nyaŋgwɛ ŋgbanja",
+            "kuŋgwɛ",
+            "fɛ",
+            "njapi",
+            "nyukul",
+            "M11",
+            "ɓulɓusɛ",
+        ],
+        [
+            "pamba",
+            "wanja",
+            "mbiyɔ mɛndoŋgɔ",
+            "Nyɔlɔmbɔŋgɔ",
+            "Mɔnɔ ŋgbanja",
+            "Nyaŋgwɛ ŋgbanja",
+            "kuŋgwɛ",
+            "fɛ",
+            "njapi",
+            "nyukul",
+            "M11",
+            "ɓulɓusɛ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "sɔndi",
+            "lundi",
+            "mardi",
+            "mɛrkɛrɛdi",
+            "yedi",
+            "vaŋdɛrɛdi",
+            "mɔnɔ sɔndi",
+        ],
+        [
+            "sɔndi",
+            "lundi",
+            "mardi",
+            "mɛrkɛrɛdi",
+            "yedi",
+            "vaŋdɛrɛdi",
+            "mɔnɔ sɔndi",
+        ],
+        [
+            "sɔndi",
+            "lundi",
+            "mardi",
+            "mɛrkɛrɛdi",
+            "yedi",
+            "vaŋdɛrɛdi",
+            "mɔnɔ sɔndi",
+        ],
+        ["so", "lu", "ma", "mɛ", "ye", "va", "ms"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `kl` locale.
+pub static KL: Locale<'static> = Locale {
+    lang: "kl",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januaarip",
+            "februaarip",
+            "marsip",
+            "apriilip",
+            "maajip",
+            "juunip",
+            "juulip",
+            "aggustip",
+            "septembarip",
+            "oktobarip",
+            "novembarip",
+            "decembarip",
+        ],
+        [
+            "jan", "febr", "mar", "apr", "maj", "jun", "jul", "aug", "sept", "okt", "nov", "dec",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "sapaat",
+            "ataasinngorneq",
+            "marlunngorneq",
+            "pingasunngorneq",
+            "sisamanngorneq",
+            "tallimanngorneq",
+            "arfininngorneq",
+        ],
+        ["sap", "ata", "mar", "pin", "sis", "tal", "arf"],
+        ["sap", "ata", "mar", "pin", "sis", "tal", "arf"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `kln` locale.
+pub static KLN: Locale<'static> = Locale {
+    lang: "kln",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mulgul",
+            "Ng’atyaato",
+            "Kiptaamo",
+            "Iwootkuut",
+            "Mamuut",
+            "Paagi",
+            "Ng’eiyeet",
+            "Rooptui",
+            "Bureet",
+            "Epeeso",
+            "Kipsuunde ne taai",
+            "Kipsuunde nebo aeng’",
+        ],
+        [
+            "Mul", "Ngat", "Taa", "Iwo", "Mam", "Paa", "Nge", "Roo", "Bur", "Epe", "Kpt", "Kpa",
+        ],
+        ["M", "N", "T", "I", "M", "P", "N", "R", "B", "E", "K", "K"],
+    ),
+    days: Locale::new_days(
+        [
+            "Kotisap",
+            "Kotaai",
+            "Koaeng’",
+            "Kosomok",
+            "Koang’wan",
+            "Komuut",
+            "Kolo",
+        ],
+        ["Kts", "Kot", "Koo", "Kos", "Koa", "Kom", "Kol"],
+        ["Kts", "Kot", "Koo", "Kos", "Koa", "Kom", "Kol"],
+        ["T", "T", "O", "S", "A", "M", "L"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["karoon", "kooskoliny"],
+        ["krn", "koosk"],
+        ["krn", "koosk"],
+    ),
+    eras: Locale::new_eras("AM", "KO"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `km` locale.
+pub static KM: Locale<'static> = Locale {
+    lang: "km",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "មករា",
+            "កុម្ភៈ",
+            "មីនា",
+            "មេសា",
+            "ឧសភា",
+            "មិថុនា",
+            "កក្កដា",
+            "សីហា",
+            "កញ្ញា",
+            "តុលា",
+            "វិច្ឆិកា",
+            "ធ្នូ",
+        ],
+        [
+            "មករា",
+            "កុម្ភៈ",
+            "មីនា",
+            "មេសា",
+            "ឧសភា",
+            "មិថុនា",
+            "កក្កដា",
+            "សីហា",
+            "កញ្ញា",
+            "តុលា",
+            "វិច្ឆិកា",
+            "ធ្នូ",
+        ],
+        ["ម", "ក", "ម", "ម", "ឧ", "ម", "ក", "ស", "ក", "ត", "វ", "ធ"],
+    ),
+    days: Locale::new_days(
+        ["អាទិត្យ", "ច័ន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍"],
+        ["អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហ", "សុក្រ", "សៅរ៍"],
+        ["អា", "ច", "អ", "ពុ", "ព្រ", "សុ", "ស"],
+        ["អ", "ច", "អ", "ព", "ព", "ស", "ស"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("មុន គ.ស.", "គ.ស."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kn` locale.
+pub static KN: Locale<'static> = Locale {
+    lang: "kn",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "d/M/yy"),
+    times: Locale::new_patterns("hh:mm:ss a zzzz", "hh:mm:ss a z", "hh:mm:ss a", "hh:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ಜನವರಿ",
+            "ಫೆಬ್ರವರಿ",
+            "ಮಾರ್ಚ್",
+            "ಏಪ್ರಿಲ್",
+            "ಮೇ",
+            "ಜೂನ್",
+            "ಜುಲೈ",
+            "ಆಗಸ್ಟ್",
+            "ಸೆಪ್ಟೆಂಬರ್",
+            "ಅಕ್ಟೋಬರ್",
+            "ನವೆಂಬರ್",
+            "ಡಿಸೆಂಬರ್",
+        ],
+        [
+            "ಜನ",
+            "ಫೆಬ್ರ",
+            "ಮಾರ್ಚ್",
+            "ಏಪ್ರಿ",
+            "ಮೇ",
+            "ಜೂನ್",
+            "ಜುಲೈ",
+            "ಆಗ",
+            "ಸೆಪ್ಟೆಂ",
+            "ಅಕ್ಟೋ",
+            "ನವೆಂ",
+            "ಡಿಸೆಂ",
+        ],
+        ["ಜ", "ಫೆ", "ಮಾ", "ಏ", "ಮೇ", "ಜೂ", "ಜು", "ಆ", "ಸೆ", "ಅ", "ನ", "ಡಿ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ಭಾನುವಾರ",
+            "ಸೋಮವಾರ",
+            "ಮಂಗಳವಾರ",
+            "ಬುಧವಾರ",
+            "ಗುರುವಾರ",
+            "ಶುಕ್ರವಾರ",
+            "ಶನಿವಾರ",
+        ],
+        ["ಭಾನು", "ಸೋಮ", "ಮಂಗಳ", "ಬುಧ", "ಗುರು", "ಶುಕ್ರ", "ಶನಿ"],
+        ["ಭಾನು", "ಸೋಮ", "ಮಂಗಳ", "ಬುಧ", "ಗುರು", "ಶುಕ್ರ", "ಶನಿ"],
+        ["ಭಾ", "ಸೋ", "ಮಂ", "ಬು", "ಗು", "ಶು", "ಶ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("ಕ್ರಿ.ಪೂ", "ಕ್ರಿ.ಶ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ko` locale.
+pub static KO: Locale<'static> = Locale {
+    lang: "ko",
+    dates: Locale::new_patterns("y년 MMMM d일 EEEE", "y년 MMMM d일", "y. M. d.", "yy. M. d."),
+    times: Locale::new_patterns(
+        "a h시 m분 s초 zzzz",
+        "a h시 m분 s초 z",
+        "a h:mm:ss",
+        "a h:mm",
+    ),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "일요일",
+            "월요일",
+            "화요일",
+            "수요일",
+            "목요일",
+            "금요일",
+            "토요일",
+        ],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+    ),
+    day_periods: Locale::new_day_periods(["오전", "오후"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ko-CN` locale.
+pub static KO_CN: Locale<'static> = Locale {
+    lang: "ko-CN",
+    dates: Locale::new_patterns("y년 MMMM d일 EEEE", "y년 MMMM d일", "y. M. d.", "yy. M. d."),
+    times: Locale::new_patterns("zzzz HH:mm:ss", "z HH:mm:ss", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "일요일",
+            "월요일",
+            "화요일",
+            "수요일",
+            "목요일",
+            "금요일",
+            "토요일",
+        ],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+    ),
+    day_periods: Locale::new_day_periods(["오전", "오후"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ko-KP` locale.
+pub static KO_KP: Locale<'static> = Locale {
+    lang: "ko-KP",
+    dates: Locale::new_patterns("y년 MMMM d일 EEEE", "y년 MMMM d일", "y. M. d.", "yy. M. d."),
+    times: Locale::new_patterns(
+        "a h시 m분 s초 zzzz",
+        "a h시 m분 s초 z",
+        "a h:mm:ss",
+        "a h:mm",
+    ),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+        [
+            "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "일요일",
+            "월요일",
+            "화요일",
+            "수요일",
+            "목요일",
+            "금요일",
+            "토요일",
+        ],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+        ["일", "월", "화", "수", "목", "금", "토"],
+    ),
+    day_periods: Locale::new_day_periods(["오전", "오후"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kok` locale.
+pub static KOK: Locale<'static> = Locale {
+    lang: "kok",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d-MMM-y", "d-M-yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "जानेवारी",
+            "फेब्रुवारी",
+            "मार्च",
+            "एप्रील",
+            "मे",
+            "जून",
+            "जुलय",
+            "ऑगस्ट",
+            "सप्टेंबर",
+            "ऑक्टोबर",
+            "नोव्हेंबर",
+            "डिसेंबर",
+        ],
+        [
+            "जाने",
+            "फेब्रु",
+            "मार्च",
+            "एप्री",
+            "मे",
+            "जून",
+            "जुल",
+            "ऑग",
+            "सप्टें",
+            "ऑक्टो",
+            "नोव्हें",
+            "डिसें",
+        ],
+        [
+            "जा", "फे", "मा", "ए", "मे", "जू", "जु", "ऑ", "स", "ऑ", "नो", "डि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "आयतार",
+            "सोमार",
+            "मंगळार",
+            "बुधवार",
+            "बिरेस्तार",
+            "शुक्रार",
+            "शेनवार",
+        ],
+        [
+            "आयतार",
+            "सोमार",
+            "मंगळार",
+            "बुधवार",
+            "बिरेस्तार",
+            "शुक्रार",
+            "शेनवार",
+        ],
+        ["आ", "सोम", "मंगळार", "बुधवार", "बिरे", "शुक्रार", "शेनवार"],
+        ["आ", "सो", "मं", "बु", "बि", "शु", "शे"],
+    ),
+    day_periods: Locale::new_day_periods(["सकाळीं", "सांजे"], ["AM", "PM"], ["a", "PM"]),
+    eras: Locale::new_eras("क्रिस्तपूर्व", "क्रि.श."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kok-Deva` locale.
+pub static KOK_DEVA: Locale<'static> = Locale {
+    lang: "kok-Deva",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d-MMM-y", "d-M-yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "जानेवारी",
+            "फेब्रुवारी",
+            "मार्च",
+            "एप्रील",
+            "मे",
+            "जून",
+            "जुलय",
+            "ऑगस्ट",
+            "सप्टेंबर",
+            "ऑक्टोबर",
+            "नोव्हेंबर",
+            "डिसेंबर",
+        ],
+        [
+            "जाने",
+            "फेब्रु",
+            "मार्च",
+            "एप्री",
+            "मे",
+            "जून",
+            "जुल",
+            "ऑग",
+            "सप्टें",
+            "ऑक्टो",
+            "नोव्हें",
+            "डिसें",
+        ],
+        [
+            "जा", "फे", "मा", "ए", "मे", "जू", "जु", "ऑ", "स", "ऑ", "नो", "डि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "आयतार",
+            "सोमार",
+            "मंगळार",
+            "बुधवार",
+            "बिरेस्तार",
+            "शुक्रार",
+            "शेनवार",
+        ],
+        [
+            "आयतार",
+            "सोमार",
+            "मंगळार",
+            "बुधवार",
+            "बिरेस्तार",
+            "शुक्रार",
+            "शेनवार",
+        ],
+        ["आ", "सोम", "मंगळार", "बुधवार", "बिरे", "शुक्रार", "शेनवार"],
+        ["आ", "सो", "मं", "बु", "बि", "शु", "शे"],
+    ),
+    day_periods: Locale::new_day_periods(["सकाळीं", "सांजे"], ["AM", "PM"], ["a", "PM"]),
+    eras: Locale::new_eras("क्रिस्तपूर्व", "क्रि.श."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kok-Latn` locale.
+pub static KOK_LATN: Locale<'static> = Locale {
+    lang: "kok-Latn",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d-M-yy"),
+    times: Locale::new_patterns("a h:mm:ss zzzz", "a h:mm:ss z", "a h:mm:ss", "a h:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janer", "Febrer", "Mars", "Abril", "Mai", "Jun", "Julai", "Agost", "Setembr", "Otubr",
+            "Novembr", "Dezembr",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Otu", "Nov", "Dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Aitar", "Somar", "Mongllar", "Budhvar", "Birestar", "Sukrar", "Sonvar",
+        ],
+        ["Ait", "Som", "Mon", "Bud", "Bre", "Suk", "Son"],
+        ["Ai", "Sm", "Mg", "Bu", "Br", "Su", "Sn"],
+        ["A", "S", "M", "B", "B", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["sokallim", "sanje"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("KA", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kpe` locale.
+pub static KPE: Locale<'static> = Locale {
+    lang: "kpe",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kpe-GN` locale.
+pub static KPE_GN: Locale<'static> = Locale {
+    lang: "kpe-GN",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ks` locale.
+pub static KS: Locale<'static> = Locale {
+    lang: "ks",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "جنؤری",
+            "فرؤری",
+            "مارٕچ",
+            "اپریل",
+            "مئی",
+            "جوٗن",
+            "جُلَے",
+            "اگست",
+            "ستمبر",
+            "اکتوٗبر",
+            "نومبر",
+            "دَسَمبَر",
+        ],
+        [
+            "جنؤری",
+            "فرؤری",
+            "مارٕچ",
+            "اپریل",
+            "مئی",
+            "جوٗن",
+            "جُلَے",
+            "اگست",
+            "ستمبر",
+            "اکتوٗبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["ج", "ف", "م", "ا", "م", "ج", "ج", "ا", "س", "س", "ا", "ن"],
+    ),
+    days: Locale::new_days(
+        [
+            "اَتھوار",
+            "ژٔندرٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        [
+            "آتھوار",
+            "ژٔندٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        [
+            "آتھوار",
+            "ژٔندٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        ["ا", "ژ", "ب", "ب", "ب", "ج", "ب"],
+    ),
+    day_periods: Locale::new_day_periods(["اے ایم", "پی ایم"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("بی سی", "اے ڈی"),
+    decimal: '.',
+    group: '،',
+};
+/// Data for `ks-Arab` locale.
+pub static KS_ARAB: Locale<'static> = Locale {
+    lang: "ks-Arab",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "جنؤری",
+            "فرؤری",
+            "مارٕچ",
+            "اپریل",
+            "مئی",
+            "جوٗن",
+            "جُلَے",
+            "اگست",
+            "ستمبر",
+            "اکتوٗبر",
+            "نومبر",
+            "دَسَمبَر",
+        ],
+        [
+            "جنؤری",
+            "فرؤری",
+            "مارٕچ",
+            "اپریل",
+            "مئی",
+            "جوٗن",
+            "جُلَے",
+            "اگست",
+            "ستمبر",
+            "اکتوٗبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["ج", "ف", "م", "ا", "م", "ج", "ج", "ا", "س", "س", "ا", "ن"],
+    ),
+    days: Locale::new_days(
+        [
+            "اَتھوار",
+            "ژٔندرٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        [
+            "آتھوار",
+            "ژٔندٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        [
+            "آتھوار",
+            "ژٔندٕروار",
+            "بۆموار",
+            "بودوار",
+            "برؠسوار",
+            "جُمہ",
+            "بٹوار",
+        ],
+        ["ا", "ژ", "ب", "ب", "ب", "ج", "ب"],
+    ),
+    day_periods: Locale::new_day_periods(["اے ایم", "پی ایم"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("بی سی", "اے ڈی"),
+    decimal: '.',
+    group: '،',
+};
+/// Data for `ks-Deva` locale.
+pub static KS_DEVA: Locale<'static> = Locale {
+    lang: "ks-Deva",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("a h:mm:ss zzzz", "a h:mm:ss z", "a h:mm:ss", "a h:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फ़रवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सतमबर",
+            "अक्तूबर",
+            "नवमबर",
+            "दसमबर",
+        ],
+        [
+            "जनवरी",
+            "फ़रवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सतुंबर",
+            "अक्तूबर",
+            "नवूमबर",
+            "दसमबर",
+        ],
+        ["ज", "फ़", "म", "अ", "म", "ज", "ज", "अ", "स", "ओ", "न", "द"],
+    ),
+    days: Locale::new_days(
+        [
+            "आथवार",
+            "च़ंदिरवार",
+            "बोमवार",
+            "बोदवार",
+            "ब्रेसवार",
+            "जुमा",
+            "बटवार",
+        ],
+        [
+            "आथवार",
+            "चंदिरवार",
+            "बुवार",
+            "बोदवार",
+            "ब्रेसवार",
+            "जुमा",
+            "बटवार",
+        ],
+        [
+            "आथवार",
+            "चंदिरवार",
+            "बुवार",
+            "बोदवार",
+            "ब्रेसवार",
+            "जुमा",
+            "बटवार",
+        ],
+        ["अ", "च", "ब", "ब", "ब", "ज", "ब"],
+    ),
+    day_periods: Locale::new_day_periods(["ये एम", "पी एम"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ksb` locale.
+pub static KSB: Locale<'static> = Locale {
+    lang: "ksb",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januali", "Febluali", "Machi", "Aplili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumaapii",
+            "Jumaatatu",
+            "Jumaane",
+            "Jumaatano",
+            "Alhamisi",
+            "Ijumaa",
+            "Jumaamosi",
+        ],
+        ["Jpi", "Jtt", "Jmn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jmn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["2", "3", "4", "5", "A", "I", "1"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["makeo", "nyiaghuo"],
+        ["makeo", "nyiaghuo"],
+        ["makeo", "nyiaghuo"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ksf` locale.
+pub static KSF: Locale<'static> = Locale {
+    lang: "ksf",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ŋwíí a ntɔ́ntɔ",
+            "ŋwíí akǝ bɛ́ɛ",
+            "ŋwíí akǝ ráá",
+            "ŋwíí akǝ nin",
+            "ŋwíí akǝ táan",
+            "ŋwíí akǝ táafɔk",
+            "ŋwíí akǝ táabɛɛ",
+            "ŋwíí akǝ táaraa",
+            "ŋwíí akǝ táanin",
+            "ŋwíí akǝ ntɛk",
+            "ŋwíí akǝ ntɛk di bɔ́k",
+            "ŋwíí akǝ ntɛk di bɛ́ɛ",
+        ],
+        [
+            "ŋ1", "ŋ2", "ŋ3", "ŋ4", "ŋ5", "ŋ6", "ŋ7", "ŋ8", "ŋ9", "ŋ10", "ŋ11", "ŋ12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "sɔ́ndǝ",
+            "lǝndí",
+            "maadí",
+            "mɛkrɛdí",
+            "jǝǝdí",
+            "júmbá",
+            "samdí",
+        ],
+        ["sɔ́n", "lǝn", "maa", "mɛk", "jǝǝ", "júm", "sam"],
+        ["sɔ́n", "lǝn", "maa", "mɛk", "jǝǝ", "júm", "sam"],
+        ["s", "l", "m", "m", "j", "j", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["sárúwá", "cɛɛ́nko"],
+        ["sárúwá", "cɛɛ́nko"],
+        ["sárúwá", "cɛɛ́nko"],
+    ),
+    eras: Locale::new_eras("d.Y.", "k.Y."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ksh` locale.
+pub static KSH: Locale<'static> = Locale {
+    lang: "ksh",
+    dates: Locale::new_patterns("EEEE, 'dä' d. MMMM y", "d. MMMM y", "d. MMM. y", "d. M. y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jannewa",
+            "Fäbrowa",
+            "Määz",
+            "Aprell",
+            "Mai",
+            "Juuni",
+            "Juuli",
+            "Oujoß",
+            "Septämber",
+            "Oktohber",
+            "Novämber",
+            "Dezämber",
+        ],
+        [
+            "Jan", "Fäb", "Mäz", "Apr", "Mai", "Jun", "Jul", "Ouj", "Säp", "Okt", "Nov", "Dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunndaach",
+            "Mohndaach",
+            "Dinnsdaach",
+            "Metwoch",
+            "Dunnersdaach",
+            "Friidaach",
+            "Samsdaach",
+        ],
+        ["Su.", "Mo.", "Di.", "Me.", "Du.", "Fr.", "Sa."],
+        ["Su", "Mo", "Di", "Me", "Du", "Fr", "Sa"],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Uhr vörmiddaachs", "Uhr nommendaachs"],
+        ["v.M.", "n.M."],
+        ["v.M.", "n.M."],
+    ),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ku` locale.
+pub static KU: Locale<'static> = Locale {
+    lang: "ku",
+    dates: Locale::new_patterns(
+        "EEEE, d'ê' MMMM'a' y'an'",
+        "d'ê' MMMM'a' y'an'",
+        "d MMM, y",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rêbendan",
+            "sibat",
+            "adar",
+            "nîsan",
+            "gulan",
+            "hezîran",
+            "tîrmeh",
+            "tebax",
+            "îlon",
+            "cotmeh",
+            "mijdar",
+            "berfanbar",
+        ],
+        [
+            "rbn", "sbt", "adr", "nsn", "gln", "hzr", "trm", "tbx", "îln", "cot", "mjd", "brf",
+        ],
+        ["R", "S", "A", "N", "G", "H", "T", "T", "Î", "C", "M", "B"],
+    ),
+    days: Locale::new_days(
+        [
+            "yekşem",
+            "duşem",
+            "sêşem",
+            "çarşem",
+            "pêncşem",
+            "înî",
+            "şemî",
+        ],
+        ["yşm", "dşm", "sşm", "çşm", "pşm", "înî", "şem"],
+        ["yş", "dş", "sş", "çş", "pş", "în", "şm"],
+        ["Y", "D", "S", "Ç", "P", "Î", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(["BN", "PN"], ["BN", "PN"], ["bn", "pn"]),
+    eras: Locale::new_eras("BM", "PM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ku-Arab` locale.
+pub static KU_ARAB: Locale<'static> = Locale {
+    lang: "ku-Arab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ku-Arab-IR` locale.
+pub static KU_ARAB_IR: Locale<'static> = Locale {
+    lang: "ku-Arab-IR",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ku-Latn` locale.
+pub static KU_LATN: Locale<'static> = Locale {
+    lang: "ku-Latn",
+    dates: Locale::new_patterns(
+        "EEEE, d'ê' MMMM'a' y'an'",
+        "d'ê' MMMM'a' y'an'",
+        "d MMM, y",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rêbendan",
+            "sibat",
+            "adar",
+            "nîsan",
+            "gulan",
+            "hezîran",
+            "tîrmeh",
+            "tebax",
+            "îlon",
+            "cotmeh",
+            "mijdar",
+            "berfanbar",
+        ],
+        [
+            "rbn", "sbt", "adr", "nsn", "gln", "hzr", "trm", "tbx", "îln", "cot", "mjd", "brf",
+        ],
+        ["R", "S", "A", "N", "G", "H", "T", "T", "Î", "C", "M", "B"],
+    ),
+    days: Locale::new_days(
+        [
+            "yekşem",
+            "duşem",
+            "sêşem",
+            "çarşem",
+            "pêncşem",
+            "înî",
+            "şemî",
+        ],
+        ["yşm", "dşm", "sşm", "çşm", "pşm", "înî", "şem"],
+        ["yş", "dş", "sş", "çş", "pş", "în", "şm"],
+        ["Y", "D", "S", "Ç", "P", "Î", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(["BN", "PN"], ["BN", "PN"], ["bn", "pn"]),
+    eras: Locale::new_eras("BM", "PM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ku-Latn-IQ` locale.
+pub static KU_LATN_IQ: Locale<'static> = Locale {
+    lang: "ku-Latn-IQ",
+    dates: Locale::new_patterns(
+        "EEEE, d'ê' MMMM'a' y'an'",
+        "d'ê' MMMM'a' y'an'",
+        "d MMM, y",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rêbendan",
+            "sibat",
+            "adar",
+            "nîsan",
+            "gulan",
+            "hezîran",
+            "tîrmeh",
+            "tebax",
+            "îlon",
+            "cotmeh",
+            "mijdar",
+            "berfanbar",
+        ],
+        [
+            "rbn", "sbt", "adr", "nsn", "gln", "hzr", "trm", "tbx", "îln", "cot", "mjd", "brf",
+        ],
+        ["R", "S", "A", "N", "G", "H", "T", "T", "Î", "C", "M", "B"],
+    ),
+    days: Locale::new_days(
+        [
+            "yekşem",
+            "duşem",
+            "sêşem",
+            "çarşem",
+            "pêncşem",
+            "înî",
+            "şemî",
+        ],
+        ["yşm", "dşm", "sşm", "çşm", "pşm", "înî", "şem"],
+        ["yş", "dş", "sş", "çş", "pş", "în", "şm"],
+        ["Y", "D", "S", "Ç", "P", "Î", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(["BN", "PN"], ["BN", "PN"], ["bn", "pn"]),
+    eras: Locale::new_eras("BM", "PM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ku-Latn-SY` locale.
+pub static KU_LATN_SY: Locale<'static> = Locale {
+    lang: "ku-Latn-SY",
+    dates: Locale::new_patterns(
+        "EEEE, d'ê' MMMM'a' y'an'",
+        "d'ê' MMMM'a' y'an'",
+        "d MMM, y",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rêbendan",
+            "sibat",
+            "adar",
+            "nîsan",
+            "gulan",
+            "hezîran",
+            "tîrmeh",
+            "tebax",
+            "îlon",
+            "cotmeh",
+            "mijdar",
+            "berfanbar",
+        ],
+        [
+            "rbn", "sbt", "adr", "nsn", "gln", "hzr", "trm", "tbx", "îln", "cot", "mjd", "brf",
+        ],
+        ["R", "S", "A", "N", "G", "H", "T", "T", "Î", "C", "M", "B"],
+    ),
+    days: Locale::new_days(
+        [
+            "yekşem",
+            "duşem",
+            "sêşem",
+            "çarşem",
+            "pêncşem",
+            "înî",
+            "şemî",
+        ],
+        ["yşm", "dşm", "sşm", "çşm", "pşm", "înî", "şem"],
+        ["yş", "dş", "sş", "çş", "pş", "în", "şm"],
+        ["Y", "D", "S", "Ç", "P", "Î", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(["BN", "PN"], ["BN", "PN"], ["bn", "pn"]),
+    eras: Locale::new_eras("BM", "PM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ku-TR` locale.
+pub static KU_TR: Locale<'static> = Locale {
+    lang: "ku-TR",
+    dates: Locale::new_patterns(
+        "EEEE, d'ê' MMMM'a' y'an'",
+        "d'ê' MMMM'a' y'an'",
+        "d MMM, y",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rêbendan",
+            "sibat",
+            "adar",
+            "nîsan",
+            "gulan",
+            "hezîran",
+            "tîrmeh",
+            "tebax",
+            "îlon",
+            "cotmeh",
+            "mijdar",
+            "berfanbar",
+        ],
+        [
+            "rbn", "sbt", "adr", "nsn", "gln", "hzr", "trm", "tbx", "îln", "cot", "mjd", "brf",
+        ],
+        ["R", "S", "A", "N", "G", "H", "T", "T", "Î", "C", "M", "B"],
+    ),
+    days: Locale::new_days(
+        [
+            "yekşem",
+            "duşem",
+            "sêşem",
+            "çarşem",
+            "pêncşem",
+            "înî",
+            "şemî",
+        ],
+        ["yşm", "dşm", "sşm", "çşm", "pşm", "înî", "şem"],
+        ["yş", "dş", "sş", "çş", "pş", "în", "şm"],
+        ["Y", "D", "S", "Ç", "P", "Î", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(["BN", "PN"], ["BN", "PN"], ["bn", "pn"]),
+    eras: Locale::new_eras("BM", "PM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `kw` locale.
+pub static KW: Locale<'static> = Locale {
+    lang: "kw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "mis Genver",
+            "mis Hwevrer",
+            "mis Meurth",
+            "mis Ebrel",
+            "mis Me",
+            "mis Metheven",
+            "mis Gortheren",
+            "mis Est",
+            "mis Gwynngala",
+            "mis Hedra",
+            "mis Du",
+            "mis Kevardhu",
+        ],
+        [
+            "Gen", "Hwe", "Meu", "Ebr", "Me", "Met", "Gor", "Est", "Gwn", "Hed", "Du", "Kev",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "dy Sul",
+            "dy Lun",
+            "dy Meurth",
+            "dy Merher",
+            "dy Yow",
+            "dy Gwener",
+            "dy Sadorn",
+        ],
+        ["Sul", "Lun", "Mth", "Mhr", "Yow", "Gwe", "Sad"],
+        ["Sul", "Lun", "Mth", "Mhr", "Yow", "Gwe", "Sad"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("RC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kxv` locale.
+pub static KXV: Locale<'static> = Locale {
+    lang: "kxv",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "pusu lenju",
+            "maha lenju",
+            "pagu lenju",
+            "hire lenju",
+            "bese lenju",
+            "jaṭṭa lenju",
+            "aasaḍi lenju",
+            "srabĩ lenju",
+            "bado lenju",
+            "dasara lenju",
+            "divi lenju",
+            "pande lenju",
+        ],
+        [
+            "pusu",
+            "maha",
+            "pagu",
+            "hire",
+            "bese",
+            "jaṭṭa",
+            "aasaḍi",
+            "srabĩ",
+            "bado",
+            "dasara",
+            "divi",
+            "pande",
+        ],
+        [
+            "pu", "ma", "pa", "hi", "be", "ja", "aa", "sra", "b", "da", "di", "pa",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "aadi vara",
+            "smbara",
+            "mangaḍa",
+            "pudara",
+            "laki vara",
+            "sukru vara",
+            "sani vara",
+        ],
+        ["aadi", "smba", "manga", "puda", "laki", "sukru", "sani"],
+        ["aa", "s", "ma", "pu", "laki", "su", "sa"],
+        ["aa", "s", "ma", "pu", "la", "su", "sa"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("bc", "ad"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kxv-Deva` locale.
+pub static KXV_DEVA: Locale<'static> = Locale {
+    lang: "kxv-Deva",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} आँ {0}", "{1} आँ {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "पुसु लेञ्जु",
+            "माहाका लेञ्जु",
+            "पागुणी लेञ्जु",
+            "हिरे लेञ्जु",
+            "बेसे लेञ्जु",
+            "जाटा लेञ्जु",
+            "आसाड़ी लेञ्जु",
+            "स्राबाँ लेञ्जु",
+            "बोदो लेञ्जु",
+            "दसारा लेञ्जु",
+            "दिवी लेञ्जु",
+            "पान्डे लेञ्जु",
+        ],
+        [
+            "पुसु",
+            "माहा",
+            "पागु",
+            "हिर्रे",
+            "बेसे",
+            "जाट्टा",
+            "आसाड़ी",
+            "स्राबाँ",
+            "बाॅदो",
+            "दासारा",
+            "दिवी",
+            "पान्डे",
+        ],
+        [
+            "पु",
+            "मा",
+            "पा",
+            "हि",
+            "बे",
+            "जा",
+            "आ",
+            "स्रा",
+            "बाॅ",
+            "दा",
+            "दि",
+            "पा",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "आदि वारा",
+            "साॅम्वारा",
+            "मंगाड़ा",
+            "पुद्दारा",
+            "लाक्कि वारा",
+            "सुकुरु वारा",
+            "सान्नि वारा",
+        ],
+        ["आदि", "साॅम्मा", "मान्गा", "पूदा", "लाक्की", "सुकुरु", "सान्नि"],
+        ["आ", "साॅ", "मा", "पू", "ला", "सु", "सा"],
+        ["आ", "साॅ", "मा", "पू", "ला", "सु", "सा"],
+    ),
+    day_periods: Locale::new_day_periods(["ए एम", "पी एम"], ["ए एम", "पी एम"], ["ए एम", "पी एम"]),
+    eras: Locale::new_eras("बिसि", "ए-डि"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kxv-Latn` locale.
+pub static KXV_LATN: Locale<'static> = Locale {
+    lang: "kxv-Latn",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "pusu lenju",
+            "maha lenju",
+            "pagu lenju",
+            "hire lenju",
+            "bese lenju",
+            "jaṭṭa lenju",
+            "aasaḍi lenju",
+            "srabĩ lenju",
+            "bado lenju",
+            "dasara lenju",
+            "divi lenju",
+            "pande lenju",
+        ],
+        [
+            "pusu",
+            "maha",
+            "pagu",
+            "hire",
+            "bese",
+            "jaṭṭa",
+            "aasaḍi",
+            "srabĩ",
+            "bado",
+            "dasara",
+            "divi",
+            "pande",
+        ],
+        [
+            "pu", "ma", "pa", "hi", "be", "ja", "aa", "sra", "b", "da", "di", "pa",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "aadi vara",
+            "smbara",
+            "mangaḍa",
+            "pudara",
+            "laki vara",
+            "sukru vara",
+            "sani vara",
+        ],
+        ["aadi", "smba", "manga", "puda", "laki", "sukru", "sani"],
+        ["aa", "s", "ma", "pu", "laki", "su", "sa"],
+        ["aa", "s", "ma", "pu", "la", "su", "sa"],
+    ),
+    day_periods: Locale::new_day_periods(["am", "pm"], ["am", "pm"], ["a", "p"]),
+    eras: Locale::new_eras("bc", "ad"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kxv-Orya` locale.
+pub static KXV_ORYA: Locale<'static> = Locale {
+    lang: "kxv-Orya",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} ଆଁ {0}", "{1} ଆଁ {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ପୁସୁ ଲେଞ୍ଜୁ",
+            "ମାହାକା ଲେଞ୍ଜୁ",
+            "ପାଗୁଣି ଲେଞ୍ଜୁ",
+            "ହିରେ ଲେଞ୍ଜୁ",
+            "ବେସେ ଲେଞ୍ଜୁ",
+            "ଜାଟା ଲେଞ୍ଜୁ",
+            "ଆସାଡ଼ି ଲେଞ୍ଜୁ",
+            "ସ୍ରାବାଁ ଲେଞ୍ଜୁ",
+            "ବଦ ଲେଞ୍ଜୁ",
+            "ଦାସାରା ଲେଞ୍ଜୁ",
+            "ଦିୱିଡ଼ି ଲେଞ୍ଜୁ",
+            "ପାଣ୍ଡେ ଲେଞ୍ଜୁ",
+        ],
+        [
+            "ପୁସୁ",
+            "ମାହା",
+            "ପାଗୁ",
+            "ହିରେ",
+            "ବେସେ",
+            "ଜାଟା",
+            "ଆସାଡ଼ି",
+            "ସ୍ରାବାଁ",
+            "ବଦ",
+            "ଦାସାରା",
+            "ଦିୱି",
+            "ପାଣ୍ଡେ",
+        ],
+        ["ପୁ", "ମା", "ପା", "ହି", "ବେ", "ଜା", "ଆ", "ସ୍ରା", "ବ", "ଦା", "ଦି", "ପା"],
+    ),
+    days: Locale::new_days(
+        ["ଆଦି ୱାରା", "ସମ୍ବାରା", "ମାଙ୍ଗାଡ଼ା", "ପୁଦାରା", "ଲାକି ୱାରା", "ସୁକ୍ରୁ ୱାରା", "ସାନି ୱାରା"],
+        ["ଆଦି", "ସମ୍ବା", "ମାଙ୍ଗା", "ପୁଦା", "ଲାକି", "ସୁକ୍ରୁ", "ସାନି"],
+        ["ଆ", "ସ", "ମା", "ପୁ", "ଲା", "ସୁ", "ସାନି"],
+        ["ଆ", "ସ", "ମା", "ପୁ", "ଲା", "ସୁ", "ସା"],
+    ),
+    day_periods: Locale::new_day_periods(["ଏ ଏମ", "ପି ଏମ"], ["ଏ ଏମ", "ପି ଏମ"], ["ଏ", "ପି"]),
+    eras: Locale::new_eras("ବିସି", "ଏଡି"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `kxv-Telu` locale.
+pub static KXV_TELU: Locale<'static> = Locale {
+    lang: "kxv-Telu",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} త {0}", "{1} త {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "మాగ",
+            "గుండు",
+            "హిరెఇ",
+            "బెసెకి",
+            "లండి",
+            "రాత",
+            "బాన్దపాణా",
+            "బార్సి",
+            "అస్ర",
+            "దివెడి",
+            "పాండు",
+            "పుసు",
+        ],
+        [
+            "మాగ",
+            "గుండు",
+            "హిరెఇ",
+            "బెసెకి",
+            "లండి",
+            "రాత",
+            "బాన్దపాణా",
+            "బార్సి",
+            "అస్ర",
+            "దివెడి",
+            "పాండు",
+            "పుసు",
+        ],
+        ["మా", "గు", "హి", "బె", "ల", "రా", "బా", "బా", "అ", "ది", "పా", "పు"],
+    ),
+    days: Locale::new_days(
+        ["వారమి", "నమారా", "మాంగాడా", "వుదారా", "లాకివరా", "నుక్ వరా", "సానివరా"],
+        ["వారమి", "నమారా", "మాంగాడా", "వుదారా", "లాకివరా", "నుక్ వరా", "సానివరా"],
+        ["వారమి", "నమారా", "మాంగాడా", "వుదారా", "లాకివరా", "నుక్ వరా", "సానివరా"],
+        ["వా", "న", "మా", "వు", "ల", "ను", "సా"],
+    ),
+    day_periods: Locale::new_day_periods(["ఎ ఎమ్", "పి ఎమ్"], ["ఎ ఎమ్", "పి ఎమ్"], ["ఎ", "పి"]),
+    eras: Locale::new_eras("క్రీపూ", "క్రీశ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ky` locale.
+pub static KY: Locale<'static> = Locale {
+    lang: "ky",
+    dates: Locale::new_patterns(
+        "y-'ж'., d-MMMM, EEEE",
+        "y-'ж'., d-MMMM",
+        "y-'ж'., d-MMM",
+        "d/M/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "январь",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "янв.", "фев.", "мар.", "апр.", "май", "июн.", "июл.", "авг.", "сен.", "окт.", "ноя.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "жекшемби",
+            "дүйшөмбү",
+            "шейшемби",
+            "шаршемби",
+            "бейшемби",
+            "жума",
+            "ишемби",
+        ],
+        ["жек.", "дүй.", "шейш.", "шарш.", "бейш.", "жума", "ишм."],
+        ["жш.", "дш.", "шш.", "шр.", "бш.", "жм.", "иш."],
+        ["Ж", "Д", "Ш", "Ш", "Б", "Ж", "И"],
+    ),
+    day_periods: Locale::new_day_periods(["таңкы", "түштөн кийинки"], ["тң", "тк"], ["тң", "тк"]),
+    eras: Locale::new_eras("б.з.ч.", "б.з."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `la` locale.
+pub static LA: Locale<'static> = Locale {
+    lang: "la",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lag` locale.
+pub static LAG: Locale<'static> = Locale {
+    lang: "lag",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Kʉfúngatɨ",
+            "Kʉnaanɨ",
+            "Kʉkeenda",
+            "Kwiikumi",
+            "Kwiinyambála",
+            "Kwiidwaata",
+            "Kʉmʉʉnchɨ",
+            "Kʉvɨɨrɨ",
+            "Kʉsaatʉ",
+            "Kwiinyi",
+            "Kʉsaano",
+            "Kʉsasatʉ",
+        ],
+        [
+            "Fúngatɨ",
+            "Naanɨ",
+            "Keenda",
+            "Ikúmi",
+            "Inyambala",
+            "Idwaata",
+            "Mʉʉnchɨ",
+            "Vɨɨrɨ",
+            "Saatʉ",
+            "Inyi",
+            "Saano",
+            "Sasatʉ",
+        ],
+        ["F", "N", "K", "I", "I", "I", "M", "V", "S", "I", "S", "S"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapíiri",
+            "Jumatátu",
+            "Jumaíne",
+            "Jumatáano",
+            "Alamíisi",
+            "Ijumáa",
+            "Jumamóosi",
+        ],
+        ["Píili", "Táatu", "Íne", "Táano", "Alh", "Ijm", "Móosi"],
+        ["Píili", "Táatu", "Íne", "Táano", "Alh", "Ijm", "Móosi"],
+        ["P", "T", "E", "O", "A", "I", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["TOO", "MUU"], ["TOO", "MUU"], ["TOO", "MUU"]),
+    eras: Locale::new_eras("KSA", "KA"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lb` locale.
+pub static LB: Locale<'static> = Locale {
+    lang: "lb",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januar",
+            "Februar",
+            "Mäerz",
+            "Abrëll",
+            "Mee",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktober",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Jan.", "Feb.", "Mäe.", "Abr.", "Mee", "Juni", "Juli", "Aug.", "Sep.", "Okt.", "Nov.",
+            "Dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonndeg",
+            "Méindeg",
+            "Dënschdeg",
+            "Mëttwoch",
+            "Donneschdeg",
+            "Freideg",
+            "Samschdeg",
+        ],
+        ["Son.", "Méi.", "Dën.", "Mët.", "Don.", "Fre.", "Sam."],
+        ["So.", "Mé.", "Dë.", "Më.", "Do.", "Fr.", "Sa."],
+        ["S", "M", "D", "M", "D", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["moies", "nomëttes"],
+        ["moies", "nomëttes"],
+        ["mo.", "nomë."],
+    ),
+    eras: Locale::new_eras("v. Chr.", "n. Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `lg` locale.
+pub static LG: Locale<'static> = Locale {
+    lang: "lg",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janwaliyo",
+            "Febwaliyo",
+            "Marisi",
+            "Apuli",
+            "Maayi",
+            "Juuni",
+            "Julaayi",
+            "Agusito",
+            "Sebuttemba",
+            "Okitobba",
+            "Novemba",
+            "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apu", "Maa", "Juu", "Jul", "Agu", "Seb", "Oki", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sabbiiti",
+            "Balaza",
+            "Lwakubiri",
+            "Lwakusatu",
+            "Lwakuna",
+            "Lwakutaano",
+            "Lwamukaaga",
+        ],
+        ["Sab", "Bal", "Lw2", "Lw3", "Lw4", "Lw5", "Lw6"],
+        ["Sab", "Bal", "Lw2", "Lw3", "Lw4", "Lw5", "Lw6"],
+        ["S", "B", "L", "L", "L", "L", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lij` locale.
+pub static LIJ: Locale<'static> = Locale {
+    lang: "lij",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de zenâ",
+            "de frevâ",
+            "de marso",
+            "d’arvî",
+            "de mazzo",
+            "de zugno",
+            "de luggio",
+            "d’agosto",
+            "de settembre",
+            "d’ottobre",
+            "de novembre",
+            "de dexembre",
+        ],
+        [
+            "de zenâ",
+            "de frevâ",
+            "de marso",
+            "d’arvî",
+            "de mazzo",
+            "de zugno",
+            "de luggio",
+            "d’agosto",
+            "de settembre",
+            "d’ottobre",
+            "de novembre",
+            "de dexembre",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "domenega",
+            "lunesdì",
+            "mätesdì",
+            "mäcordì",
+            "zeuggia",
+            "venardì",
+            "sabbo",
+        ],
+        [
+            "domenega",
+            "lunesdì",
+            "mätesdì",
+            "mäcordì",
+            "zeuggia",
+            "venardì",
+            "sabbo",
+        ],
+        [
+            "domenega",
+            "lunesdì",
+            "mätesdì",
+            "mäcordì",
+            "zeuggia",
+            "venardì",
+            "sabbo",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `lkt` locale.
+pub static LKT: Locale<'static> = Locale {
+    lang: "lkt",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Wiótheȟika Wí",
+            "Thiyóȟeyuŋka Wí",
+            "Ištáwičhayazaŋ Wí",
+            "Pȟežítȟo Wí",
+            "Čhaŋwápetȟo Wí",
+            "Wípazukȟa-wašté Wí",
+            "Čhaŋpȟásapa Wí",
+            "Wasútȟuŋ Wí",
+            "Čhaŋwápeǧi Wí",
+            "Čhaŋwápe-kasná Wí",
+            "Waníyetu Wí",
+            "Tȟahékapšuŋ Wí",
+        ],
+        [
+            "Wiótheȟika Wí",
+            "Thiyóȟeyuŋka Wí",
+            "Ištáwičhayazaŋ Wí",
+            "Pȟežítȟo Wí",
+            "Čhaŋwápetȟo Wí",
+            "Wípazukȟa-wašté Wí",
+            "Čhaŋpȟásapa Wí",
+            "Wasútȟuŋ Wí",
+            "Čhaŋwápeǧi Wí",
+            "Čhaŋwápe-kasná Wí",
+            "Waníyetu Wí",
+            "Tȟahékapšuŋ Wí",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Aŋpétuwakȟaŋ",
+            "Aŋpétuwaŋži",
+            "Aŋpétunuŋpa",
+            "Aŋpétuyamni",
+            "Aŋpétutopa",
+            "Aŋpétuzaptaŋ",
+            "Owáŋgyužažapi",
+        ],
+        [
+            "Aŋpétuwakȟaŋ",
+            "Aŋpétuwaŋži",
+            "Aŋpétunuŋpa",
+            "Aŋpétuyamni",
+            "Aŋpétutopa",
+            "Aŋpétuzaptaŋ",
+            "Owáŋgyužažapi",
+        ],
+        [
+            "Aŋpétuwakȟaŋ",
+            "Aŋpétuwaŋži",
+            "Aŋpétunuŋpa",
+            "Aŋpétuyamni",
+            "Aŋpétutopa",
+            "Aŋpétuzaptaŋ",
+            "Owáŋgyužažapi",
+        ],
+        ["A", "W", "N", "Y", "T", "Z", "O"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lld` locale.
+pub static LLD: Locale<'static> = Locale {
+    lang: "lld",
+    dates: Locale::new_patterns("EEEE, d MMMM 'dl' y", "d MMMM y", "d MMM y", "dd.MM.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "de jená",
+            "de forá",
+            "de merz",
+            "d’aurí",
+            "de ma",
+            "de jügn",
+            "de messé",
+            "d’aost",
+            "de setëmber",
+            "d’otober",
+            "de novëmber",
+            "de dezëmber",
+        ],
+        [
+            "de jená",
+            "de forá",
+            "de merz",
+            "d’aurí",
+            "de ma",
+            "de jügn",
+            "de messé",
+            "d’aost",
+            "de setëmber",
+            "d’otober",
+            "de novëmber",
+            "de dezëmber",
+        ],
+        ["J", "F", "M", "A", "M", "J", "M", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domënia", "lönesc", "mertesc", "mercui", "jöbia", "vëndres", "sabeda",
+        ],
+        ["dom", "lön", "mert", "merc", "jöb", "vën", "sab"],
+        ["dom", "lön", "mert", "merc", "jöb", "vën", "sab"],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("dan G.C.", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `lmo` locale.
+pub static LMO: Locale<'static> = Locale {
+    lang: "lmo",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sginer", "fevrer", "marz", "avril", "masg", "sgiugn", "luj", "avost", "setember",
+            "otover", "november", "dicember",
+        ],
+        [
+            "sginer", "fevrer", "marz", "avril", "masg", "sgiugn", "luj", "avost", "setember",
+            "otover", "november", "dicember",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "domenega",
+            "lundì",
+            "mardì",
+            "mercoldì",
+            "sgiovedì",
+            "venerdì",
+            "sabet",
+        ],
+        [
+            "domenega",
+            "lundì",
+            "mardì",
+            "mercoldì",
+            "sgiovedì",
+            "venerdì",
+            "sabet",
+        ],
+        [
+            "domenega",
+            "lundì",
+            "mardì",
+            "mercoldì",
+            "sgiovedì",
+            "venerdì",
+            "sabet",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["del matin", "de sira"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '\'',
+};
+/// Data for `ln` locale.
+pub static LN: Locale<'static> = Locale {
+    lang: "ln",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sánzá ya yambo",
+            "sánzá ya míbalé",
+            "sánzá ya mísáto",
+            "sánzá ya mínei",
+            "sánzá ya mítáno",
+            "sánzá ya motóbá",
+            "sánzá ya nsambo",
+            "sánzá ya mwambe",
+            "sánzá ya libwa",
+            "sánzá ya zómi",
+            "sánzá ya zómi na mɔ̌kɔ́",
+            "sánzá ya zómi na míbalé",
+        ],
+        [
+            "yan", "fbl", "msi", "apl", "mai", "yun", "yul", "agt", "stb", "ɔtb", "nvb", "dsb",
+        ],
+        ["y", "f", "m", "a", "m", "y", "y", "a", "s", "ɔ", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "eyenga",
+            "mokɔlɔ mwa yambo",
+            "mokɔlɔ mwa míbalé",
+            "mokɔlɔ mwa mísáto",
+            "mokɔlɔ ya mínéi",
+            "mokɔlɔ ya mítáno",
+            "mpɔ́sɔ",
+        ],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["e", "y", "m", "m", "m", "m", "p"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+    ),
+    eras: Locale::new_eras("libóso ya", "nsima ya Y"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ln-AO` locale.
+pub static LN_AO: Locale<'static> = Locale {
+    lang: "ln-AO",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sánzá ya yambo",
+            "sánzá ya míbalé",
+            "sánzá ya mísáto",
+            "sánzá ya mínei",
+            "sánzá ya mítáno",
+            "sánzá ya motóbá",
+            "sánzá ya nsambo",
+            "sánzá ya mwambe",
+            "sánzá ya libwa",
+            "sánzá ya zómi",
+            "sánzá ya zómi na mɔ̌kɔ́",
+            "sánzá ya zómi na míbalé",
+        ],
+        [
+            "yan", "fbl", "msi", "apl", "mai", "yun", "yul", "agt", "stb", "ɔtb", "nvb", "dsb",
+        ],
+        ["y", "f", "m", "a", "m", "y", "y", "a", "s", "ɔ", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "eyenga",
+            "mokɔlɔ mwa yambo",
+            "mokɔlɔ mwa míbalé",
+            "mokɔlɔ mwa mísáto",
+            "mokɔlɔ ya mínéi",
+            "mokɔlɔ ya mítáno",
+            "mpɔ́sɔ",
+        ],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["e", "y", "m", "m", "m", "m", "p"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+    ),
+    eras: Locale::new_eras("libóso ya", "nsima ya Y"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ln-CF` locale.
+pub static LN_CF: Locale<'static> = Locale {
+    lang: "ln-CF",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sánzá ya yambo",
+            "sánzá ya míbalé",
+            "sánzá ya mísáto",
+            "sánzá ya mínei",
+            "sánzá ya mítáno",
+            "sánzá ya motóbá",
+            "sánzá ya nsambo",
+            "sánzá ya mwambe",
+            "sánzá ya libwa",
+            "sánzá ya zómi",
+            "sánzá ya zómi na mɔ̌kɔ́",
+            "sánzá ya zómi na míbalé",
+        ],
+        [
+            "yan", "fbl", "msi", "apl", "mai", "yun", "yul", "agt", "stb", "ɔtb", "nvb", "dsb",
+        ],
+        ["y", "f", "m", "a", "m", "y", "y", "a", "s", "ɔ", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "eyenga",
+            "mokɔlɔ mwa yambo",
+            "mokɔlɔ mwa míbalé",
+            "mokɔlɔ mwa mísáto",
+            "mokɔlɔ ya mínéi",
+            "mokɔlɔ ya mítáno",
+            "mpɔ́sɔ",
+        ],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["e", "y", "m", "m", "m", "m", "p"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+    ),
+    eras: Locale::new_eras("libóso ya", "nsima ya Y"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ln-CG` locale.
+pub static LN_CG: Locale<'static> = Locale {
+    lang: "ln-CG",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sánzá ya yambo",
+            "sánzá ya míbalé",
+            "sánzá ya mísáto",
+            "sánzá ya mínei",
+            "sánzá ya mítáno",
+            "sánzá ya motóbá",
+            "sánzá ya nsambo",
+            "sánzá ya mwambe",
+            "sánzá ya libwa",
+            "sánzá ya zómi",
+            "sánzá ya zómi na mɔ̌kɔ́",
+            "sánzá ya zómi na míbalé",
+        ],
+        [
+            "yan", "fbl", "msi", "apl", "mai", "yun", "yul", "agt", "stb", "ɔtb", "nvb", "dsb",
+        ],
+        ["y", "f", "m", "a", "m", "y", "y", "a", "s", "ɔ", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "eyenga",
+            "mokɔlɔ mwa yambo",
+            "mokɔlɔ mwa míbalé",
+            "mokɔlɔ mwa mísáto",
+            "mokɔlɔ ya mínéi",
+            "mokɔlɔ ya mítáno",
+            "mpɔ́sɔ",
+        ],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["eye", "ybo", "mbl", "mst", "min", "mtn", "mps"],
+        ["e", "y", "m", "m", "m", "m", "p"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+        ["ntɔ́ngɔ́", "mpókwa"],
+    ),
+    eras: Locale::new_eras("libóso ya", "nsima ya Y"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `lo` locale.
+pub static LO: Locale<'static> = Locale {
+    lang: "lo",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns(
+        "H ໂມງ m ນາທີ ss ວິນາທີ zzzz",
+        "H ໂມງ m ນາທີ ss ວິນາທີ z",
+        "H:mm:ss",
+        "H:mm",
+    ),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ມັງກອນ",
+            "ກຸມພາ",
+            "ມີນາ",
+            "ເມສາ",
+            "ພຶດສະພາ",
+            "ມິຖຸນາ",
+            "ກໍລະກົດ",
+            "ສິງຫາ",
+            "ກັນຍາ",
+            "ຕຸລາ",
+            "ພະຈິກ",
+            "ທັນວາ",
+        ],
+        [
+            "ມ.ກ.",
+            "ກ.ພ.",
+            "ມ.ນ.",
+            "ມ.ສ.",
+            "ພ.ພ.",
+            "ມິ.ຖ.",
+            "ກ.ລ.",
+            "ສ.ຫ.",
+            "ກ.ຍ.",
+            "ຕ.ລ.",
+            "ພ.ຈ.",
+            "ທ.ວ.",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ວັນອາທິດ",
+            "ວັນຈັນ",
+            "ວັນອັງຄານ",
+            "ວັນພຸດ",
+            "ວັນພະຫັດ",
+            "ວັນສຸກ",
+            "ວັນເສົາ",
+        ],
+        ["ອາທິດ", "ຈັນ", "ອັງຄານ", "ພຸດ", "ພະຫັດ", "ສຸກ", "ເສົາ"],
+        ["ອາ.", "ຈ.", "ອ.", "ພ.", "ພຫ.", "ສຸ.", "ສ."],
+        ["ອາ", "ຈ", "ອ", "ພ", "ພຫ", "ສຸ", "ສ"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ກ່ອນທ່ຽງ", "ຫຼັງທ່ຽງ"],
+        ["ກ່ອນທ່ຽງ", "ຫຼັງທ່ຽງ"],
+        ["ກ່ອນທ່ຽງ", "ຫຼັງທ່ຽງ"],
+    ),
+    eras: Locale::new_eras("ກ່ອນ ຄ.ສ.", "ຄ.ສ."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `lrc` locale.
+pub static LRC: Locale<'static> = Locale {
+    lang: "lrc",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جانڤیە",
+            "فئڤریە",
+            "مارس",
+            "آڤریل",
+            "مئی",
+            "جوٙأن",
+            "جوٙلا",
+            "آگوست",
+            "سئپتامر",
+            "ئوکتوڤر",
+            "نوڤامر",
+            "دئسامر",
+        ],
+        [
+            "جانڤیە",
+            "فئڤریە",
+            "مارس",
+            "آڤریل",
+            "مئی",
+            "جوٙأن",
+            "جوٙلا",
+            "آگوست",
+            "سئپتامر",
+            "ئوکتوڤر",
+            "نوڤامر",
+            "دئسامر",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lrc-IQ` locale.
+pub static LRC_IQ: Locale<'static> = Locale {
+    lang: "lrc-IQ",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جانڤیە",
+            "فئڤریە",
+            "مارس",
+            "آڤریل",
+            "مئی",
+            "جوٙأن",
+            "جوٙلا",
+            "آگوست",
+            "سئپتامر",
+            "ئوکتوڤر",
+            "نوڤامر",
+            "دئسامر",
+        ],
+        [
+            "جانڤیە",
+            "فئڤریە",
+            "مارس",
+            "آڤریل",
+            "مئی",
+            "جوٙأن",
+            "جوٙلا",
+            "آگوست",
+            "سئپتامر",
+            "ئوکتوڤر",
+            "نوڤامر",
+            "دئسامر",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lt` locale.
+pub static LT: Locale<'static> = Locale {
+    lang: "lt",
+    dates: Locale::new_patterns(
+        "y 'm'. MMMM d 'd'., EEEE",
+        "y 'm'. MMMM d 'd'.",
+        "y-MM-dd",
+        "y-MM-dd",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "sausio",
+            "vasario",
+            "kovo",
+            "balandžio",
+            "gegužės",
+            "birželio",
+            "liepos",
+            "rugpjūčio",
+            "rugsėjo",
+            "spalio",
+            "lapkričio",
+            "gruodžio",
+        ],
+        [
+            "saus.", "vas.", "kov.", "bal.", "geg.", "birž.", "liep.", "rugp.", "rugs.", "spal.",
+            "lapkr.", "gruod.",
+        ],
+        ["S", "V", "K", "B", "G", "B", "L", "R", "R", "S", "L", "G"],
+    ),
+    days: Locale::new_days(
+        [
+            "sekmadienis",
+            "pirmadienis",
+            "antradienis",
+            "trečiadienis",
+            "ketvirtadienis",
+            "penktadienis",
+            "šeštadienis",
+        ],
+        ["sk", "pr", "an", "tr", "kt", "pn", "št"],
+        ["Sk", "Pr", "An", "Tr", "Kt", "Pn", "Št"],
+        ["S", "P", "A", "T", "K", "P", "Š"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["priešpiet", "popiet"],
+        ["priešpiet", "popiet"],
+        ["pr. p.", "pop."],
+    ),
+    eras: Locale::new_eras("pr. Kr.", "po Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ltg` locale.
+pub static LTG: Locale<'static> = Locale {
+    lang: "ltg",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lu` locale.
+pub static LU: Locale<'static> = Locale {
+    lang: "lu",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ciongo",
+            "Lùishi",
+            "Lusòlo",
+            "Mùuyà",
+            "Lumùngùlù",
+            "Lufuimi",
+            "Kabàlàshìpù",
+            "Lùshìkà",
+            "Lutongolo",
+            "Lungùdi",
+            "Kaswèkèsè",
+            "Ciswà",
+        ],
+        [
+            "Cio", "Lui", "Lus", "Muu", "Lum", "Luf", "Kab", "Lush", "Lut", "Lun", "Kas", "Cis",
+        ],
+        ["C", "L", "L", "M", "L", "L", "K", "L", "L", "L", "K", "C"],
+    ),
+    days: Locale::new_days(
+        [
+            "Lumingu", "Nkodya", "Ndàayà", "Ndangù", "Njòwa", "Ngòvya", "Lubingu",
+        ],
+        ["Lum", "Nko", "Ndy", "Ndg", "Njw", "Ngv", "Lub"],
+        ["Lum", "Nko", "Ndy", "Ndg", "Njw", "Ngv", "Lub"],
+        ["L", "N", "N", "N", "N", "N", "L"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Dinda", "Dilolo"],
+        ["Dinda", "Dilolo"],
+        ["Dinda", "Dilolo"],
+    ),
+    eras: Locale::new_eras("kmp. Y.K.", "kny. Y. K."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `luo` locale.
+pub static LUO: Locale<'static> = Locale {
+    lang: "luo",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Dwe mar Achiel",
+            "Dwe mar Ariyo",
+            "Dwe mar Adek",
+            "Dwe mar Ang’wen",
+            "Dwe mar Abich",
+            "Dwe mar Auchiel",
+            "Dwe mar Abiriyo",
+            "Dwe mar Aboro",
+            "Dwe mar Ochiko",
+            "Dwe mar Apar",
+            "Dwe mar gi achiel",
+            "Dwe mar Apar gi ariyo",
+        ],
+        [
+            "DAC", "DAR", "DAD", "DAN", "DAH", "DAU", "DAO", "DAB", "DOC", "DAP", "DGI", "DAG",
+        ],
+        ["C", "R", "D", "N", "B", "U", "B", "B", "C", "P", "C", "P"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapil",
+            "Wuok Tich",
+            "Tich Ariyo",
+            "Tich Adek",
+            "Tich Ang’wen",
+            "Tich Abich",
+            "Ngeso",
+        ],
+        ["JMP", "WUT", "TAR", "TAD", "TAN", "TAB", "NGS"],
+        ["JMP", "WUT", "TAR", "TAD", "TAN", "TAB", "NGS"],
+        ["J", "W", "T", "T", "T", "T", "N"],
+    ),
+    day_periods: Locale::new_day_periods(["OD", "OT"], ["OD", "OT"], ["OD", "OT"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `luy` locale.
+pub static LUY: Locale<'static> = Locale {
+    lang: "luy",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapiri",
+            "Jumatatu",
+            "Jumanne",
+            "Jumatano",
+            "Murwa wa Kanne",
+            "Murwa wa Katano",
+            "Jumamosi",
+        ],
+        ["J2", "J3", "J4", "J5", "Al", "Ij", "J1"],
+        ["J2", "J3", "J4", "J5", "Al", "Ij", "J1"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `lv` locale.
+pub static LV: Locale<'static> = Locale {
+    lang: "lv",
+    dates: Locale::new_patterns(
+        "EEEE, y. 'gada' d. MMMM",
+        "y. 'gada' d. MMMM",
+        "y. 'gada' d. MMM",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janvāris",
+            "februāris",
+            "marts",
+            "aprīlis",
+            "maijs",
+            "jūnijs",
+            "jūlijs",
+            "augusts",
+            "septembris",
+            "oktobris",
+            "novembris",
+            "decembris",
+        ],
+        [
+            "janv.", "febr.", "marts", "apr.", "maijs", "jūn.", "jūl.", "aug.", "sept.", "okt.",
+            "nov.", "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "svētdiena",
+            "pirmdiena",
+            "otrdiena",
+            "trešdiena",
+            "ceturtdiena",
+            "piektdiena",
+            "sestdiena",
+        ],
+        [
+            "svētd.", "pirmd.", "otrd.", "trešd.", "ceturtd.", "piektd.", "sestd.",
+        ],
+        ["Sv", "Pr", "Ot", "Tr", "Ce", "Pk", "Se"],
+        ["S", "P", "O", "T", "C", "P", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["priekšpusdienā", "pēcpusdienā"],
+        ["priekšp.", "pēcp."],
+        ["priekšp.", "pēcp."],
+    ),
+    eras: Locale::new_eras("p.m.ē.", "m.ē."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `lzz` locale.
+pub static LZZ: Locale<'static> = Locale {
+    lang: "lzz",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mai` locale.
+pub static MAI: Locale<'static> = Locale {
+    lang: "mai",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितंबर",
+            "अक्तूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जन॰",
+            "फ़र॰",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुल॰",
+            "अग॰",
+            "सित॰",
+            "अक्तू॰",
+            "नव॰",
+            "दिस॰",
+        ],
+        [
+            "ज", "फ", "मा", "अ", "म", "जू", "जु", "अ", "सि", "अ", "न", "दि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रवि दिन",
+            "सोम दिन",
+            "मंगल दिन",
+            "बुध दिन",
+            "बृहस्पति दिन",
+            "शुक्र दिन",
+            "शनि दिन",
+        ],
+        ["रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(["भोर", "सांझ"], ["भोर", "सांझ"], ["भोर", "सांझ"]),
+    eras: Locale::new_eras("ईसा-पूर्व", "ईसवी"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mas` locale.
+pub static MAS: Locale<'static> = Locale {
+    lang: "mas",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Oladalʉ́",
+            "Arát",
+            "Ɔɛnɨ́ɔɨŋɔk",
+            "Olodoyíóríê inkókúâ",
+            "Oloilépūnyīē inkókúâ",
+            "Kújúɔrɔk",
+            "Mórusásin",
+            "Ɔlɔ́ɨ́bɔ́rárɛ",
+            "Kúshîn",
+            "Olgísan",
+            "Pʉshʉ́ka",
+            "Ntʉ́ŋʉ́s",
+        ],
+        [
+            "Dal", "Ará", "Ɔɛn", "Doy", "Lép", "Rok", "Sás", "Bɔ́r", "Kús", "Gís", "Shʉ́", "Ntʉ́",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapílí",
+            "Jumatátu",
+            "Jumane",
+            "Jumatánɔ",
+            "Alaámisi",
+            "Jumáa",
+            "Jumamósi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["2", "3", "4", "5", "6", "7", "1"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+    ),
+    eras: Locale::new_eras("MY", "EY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mas-TZ` locale.
+pub static MAS_TZ: Locale<'static> = Locale {
+    lang: "mas-TZ",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Oladalʉ́",
+            "Arát",
+            "Ɔɛnɨ́ɔɨŋɔk",
+            "Olodoyíóríê inkókúâ",
+            "Oloilépūnyīē inkókúâ",
+            "Kújúɔrɔk",
+            "Mórusásin",
+            "Ɔlɔ́ɨ́bɔ́rárɛ",
+            "Kúshîn",
+            "Olgísan",
+            "Pʉshʉ́ka",
+            "Ntʉ́ŋʉ́s",
+        ],
+        [
+            "Dal", "Ará", "Ɔɛn", "Doy", "Lép", "Rok", "Sás", "Bɔ́r", "Kús", "Gís", "Shʉ́", "Ntʉ́",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapílí",
+            "Jumatátu",
+            "Jumane",
+            "Jumatánɔ",
+            "Alaámisi",
+            "Jumáa",
+            "Jumamósi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["2", "3", "4", "5", "6", "7", "1"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+        ["Ɛnkakɛnyá", "Ɛndámâ"],
+    ),
+    eras: Locale::new_eras("MY", "EY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mdf` locale.
+pub static MDF: Locale<'static> = Locale {
+    lang: "mdf",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mer` locale.
+pub static MER: Locale<'static> = Locale {
+    lang: "mer",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januarĩ",
+            "Feburuarĩ",
+            "Machi",
+            "Ĩpurũ",
+            "Mĩĩ",
+            "Njuni",
+            "Njuraĩ",
+            "Agasti",
+            "Septemba",
+            "Oktũba",
+            "Novemba",
+            "Dicemba",
+        ],
+        [
+            "JAN", "FEB", "MAC", "ĨPU", "MĨĨ", "NJU", "NJR", "AGA", "SPT", "OKT", "NOV", "DEC",
+        ],
+        ["J", "F", "M", "Ĩ", "M", "N", "N", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Kiumia", "Muramuko", "Wairi", "Wethatu", "Wena", "Wetano", "Jumamosi",
+        ],
+        ["KIU", "MRA", "WAI", "WET", "WEN", "WTN", "JUM"],
+        ["KIU", "MRA", "WAI", "WET", "WEN", "WTN", "JUM"],
+        ["K", "M", "W", "W", "W", "W", "J"],
+    ),
+    day_periods: Locale::new_day_periods(["RŨ", "ŨG"], ["RŨ", "ŨG"], ["RŨ", "ŨG"]),
+    eras: Locale::new_eras("MK", "NK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mfe` locale.
+pub static MFE: Locale<'static> = Locale {
+    lang: "mfe",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "zanvie", "fevriye", "mars", "avril", "me", "zin", "zilye", "out", "septam", "oktob",
+            "novam", "desam",
+        ],
+        [
+            "zan", "fev", "mar", "avr", "me", "zin", "zil", "out", "sep", "okt", "nov", "des",
+        ],
+        ["z", "f", "m", "a", "m", "z", "z", "o", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "dimans", "lindi", "mardi", "merkredi", "zedi", "vandredi", "samdi",
+        ],
+        ["dim", "lin", "mar", "mer", "ze", "van", "sam"],
+        ["dim", "lin", "mar", "mer", "ze", "van", "sam"],
+        ["d", "l", "m", "m", "z", "v", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("av. Z-K", "ap. Z-K"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `mg` locale.
+pub static MG: Locale<'static> = Locale {
+    lang: "mg",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janoary",
+            "Febroary",
+            "Martsa",
+            "Aprily",
+            "Mey",
+            "Jona",
+            "Jolay",
+            "Aogositra",
+            "Septambra",
+            "Oktobra",
+            "Novambra",
+            "Desambra",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "Mey", "Jon", "Jol", "Aog", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Alahady",
+            "Alatsinainy",
+            "Talata",
+            "Alarobia",
+            "Alakamisy",
+            "Zoma",
+            "Asabotsy",
+        ],
+        ["Alah", "Alats", "Tal", "Alar", "Alak", "Zom", "Asab"],
+        ["Alah", "Alats", "Tal", "Alar", "Alak", "Zom", "Asab"],
+        ["A", "A", "T", "A", "A", "Z", "A"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mgh` locale.
+pub static MGH: Locale<'static> = Locale {
+    lang: "mgh",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mweri wo kwanza",
+            "Mweri wo unayeli",
+            "Mweri wo uneraru",
+            "Mweri wo unecheshe",
+            "Mweri wo unethanu",
+            "Mweri wo thanu na mocha",
+            "Mweri wo saba",
+            "Mweri wo nane",
+            "Mweri wo tisa",
+            "Mweri wo kumi",
+            "Mweri wo kumi na moja",
+            "Mweri wo kumi na yel’li",
+        ],
+        [
+            "Kwa", "Una", "Rar", "Che", "Tha", "Moc", "Sab", "Nan", "Tis", "Kum", "Moj", "Yel",
+        ],
+        ["K", "U", "R", "C", "T", "M", "S", "N", "T", "K", "M", "Y"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sabato",
+            "Jumatatu",
+            "Jumanne",
+            "Jumatano",
+            "Arahamisi",
+            "Ijumaa",
+            "Jumamosi",
+        ],
+        ["Sab", "Jtt", "Jnn", "Jtn", "Ara", "Iju", "Jmo"],
+        ["Sab", "Jtt", "Jnn", "Jtn", "Ara", "Iju", "Jmo"],
+        ["S", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["wichishu", "mchochil’l"],
+        ["wichishu", "mchochil’l"],
+        ["wichishu", "mchochil’l"],
+    ),
+    eras: Locale::new_eras("HY", "YY"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `mgo` locale.
+pub static MGO: Locale<'static> = Locale {
+    lang: "mgo",
+    dates: Locale::new_patterns("EEEE, y MMMM dd", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "iməg mbegtug",
+            "imeg àbùbì",
+            "imeg mbəŋchubi",
+            "iməg ngwə̀t",
+            "iməg fog",
+            "iməg ichiibɔd",
+            "iməg àdùmbə̀ŋ",
+            "iməg ichika",
+            "iməg kud",
+            "iməg tèsiʼe",
+            "iməg zò",
+            "iməg krizmed",
+        ],
+        [
+            "mbegtug",
+            "imeg àbùbì",
+            "imeg mbəŋchubi",
+            "iməg ngwə̀t",
+            "iməg fog",
+            "iməg ichiibɔd",
+            "iməg àdùmbə̀ŋ",
+            "iməg ichika",
+            "iməg kud",
+            "iməg tèsiʼe",
+            "iməg zò",
+            "iməg krizmed",
+        ],
+        [
+            "M1", "A2", "M3", "N4", "F5", "I6", "A7", "I8", "K9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Aneg 1", "Aneg 2", "Aneg 3", "Aneg 4", "Aneg 5", "Aneg 6", "Aneg 7",
+        ],
+        [
+            "Aneg 1", "Aneg 2", "Aneg 3", "Aneg 4", "Aneg 5", "Aneg 6", "Aneg 7",
+        ],
+        ["A1", "A2", "A3", "A4", "A5", "A6", "A7"],
+        ["1", "2", "3", "4", "5", "6", "7"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mhn` locale.
+pub static MHN: Locale<'static> = Locale {
+    lang: "mhn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mi` locale.
+pub static MI: Locale<'static> = Locale {
+    lang: "mi",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Hānuere",
+            "Pēpuere",
+            "Māehe",
+            "Āpereira",
+            "Mei",
+            "Hune",
+            "Hūrae",
+            "Ākuhata",
+            "Hepetema",
+            "Oketopa",
+            "Noema",
+            "Tīhema",
+        ],
+        [
+            "Hān", "Pēp", "Māe", "Āpe", "Mei", "Hun", "Hūr", "Āku", "Hep", "Oke", "Noe", "Tīh",
+        ],
+        ["H", "P", "M", "Ā", "M", "H", "H", "Ā", "H", "O", "N", "T"],
+    ),
+    days: Locale::new_days(
+        [
+            "Rātapu", "Mane", "Tūrei", "Wenerei", "Tāite", "Paraire", "Rāhoroi",
+        ],
+        ["Rāt", "Man", "Tūr", "Wen", "Tāi", "Par", "Rāh"],
+        ["Rāt", "Man", "Tū", "Wen", "Tāi", "Par", "Rāh"],
+        ["Rt", "M", "T", "W", "T", "P", "Rh"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mic` locale.
+pub static MIC: Locale<'static> = Locale {
+    lang: "mic",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mk` locale.
+pub static MK: Locale<'static> = Locale {
+    lang: "mk",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "d.M.y 'г'.",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "јануари",
+            "февруари",
+            "март",
+            "април",
+            "мај",
+            "јуни",
+            "јули",
+            "август",
+            "септември",
+            "октомври",
+            "ноември",
+            "декември",
+        ],
+        [
+            "јан.", "фев.", "мар.", "апр.", "мај", "јун.", "јул.", "авг.", "сеп.", "окт.", "ное.",
+            "дек.",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недела",
+            "понеделник",
+            "вторник",
+            "среда",
+            "четврток",
+            "петок",
+            "сабота",
+        ],
+        ["нед.", "пон.", "вто.", "сре.", "чет.", "пет.", "саб."],
+        ["нед.", "пон.", "вто.", "сре.", "чет.", "пет.", "саб."],
+        ["н", "п", "в", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["претпл.", "попл."],
+        ["претпл.", "попл."],
+        ["претпл.", "попл."],
+    ),
+    eras: Locale::new_eras("пр. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ml` locale.
+pub static ML: Locale<'static> = Locale {
+    lang: "ml",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "d/M/yy"),
+    times: Locale::new_patterns("zzzz h:mm:ss a", "z h:mm:ss a", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ജനുവരി",
+            "ഫെബ്രുവരി",
+            "മാർച്ച്",
+            "ഏപ്രിൽ",
+            "മേയ്",
+            "ജൂൺ",
+            "ജൂലൈ",
+            "ഓഗസ്റ്റ്",
+            "സെപ്റ്റംബർ",
+            "ഒക്‌ടോബർ",
+            "നവംബർ",
+            "ഡിസംബർ",
+        ],
+        [
+            "ജനു",
+            "ഫെബ്രു",
+            "മാർ",
+            "ഏപ്രി",
+            "മേയ്",
+            "ജൂൺ",
+            "ജൂലൈ",
+            "ഓഗ",
+            "സെപ്റ്റം",
+            "ഒക്ടോ",
+            "നവം",
+            "ഡിസം",
+        ],
+        [
+            "ജ",
+            "ഫെ",
+            "മാ",
+            "ഏ",
+            "മെ",
+            "ജൂൺ",
+            "ജൂ",
+            "ഓ",
+            "സെ",
+            "ഒ",
+            "ന",
+            "ഡി",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ഞായറാഴ്‌ച",
+            "തിങ്കളാഴ്‌ച",
+            "ചൊവ്വാഴ്ച",
+            "ബുധനാഴ്‌ച",
+            "വ്യാഴാഴ്‌ച",
+            "വെള്ളിയാഴ്‌ച",
+            "ശനിയാഴ്‌ച",
+        ],
+        ["ഞായർ", "തിങ്കൾ", "ചൊവ്വ", "ബുധൻ", "വ്യാഴം", "വെള്ളി", "ശനി"],
+        ["ഞാ", "തി", "ചൊ", "ബു", "വ്യാ", "വെ", "ശ"],
+        ["ഞ", "തി", "ചൊ", "ബു", "വ്യാ", "വെ", "ശ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ബിസി", "എഡി"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mn` locale.
+pub static MN: Locale<'static> = Locale {
+    lang: "mn",
+    dates: Locale::new_patterns(
+        "y 'оны' MMMM'ын' d, EEEE 'гараг'",
+        "y 'оны' MMMM'ын' d",
+        "y 'оны' MMM'ын' d",
+        "y.MM.dd",
+    ),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "нэгдүгээр сар",
+            "хоёрдугаар сар",
+            "гуравдугаар сар",
+            "дөрөвдүгээр сар",
+            "тавдугаар сар",
+            "зургаадугаар сар",
+            "долоодугаар сар",
+            "наймдугаар сар",
+            "есдүгээр сар",
+            "аравдугаар сар",
+            "арван нэгдүгээр сар",
+            "арван хоёрдугаар сар",
+        ],
+        [
+            "1-р сар",
+            "2-р сар",
+            "3-р сар",
+            "4-р сар",
+            "5-р сар",
+            "6-р сар",
+            "7-р сар",
+            "8-р сар",
+            "9-р сар",
+            "10-р сар",
+            "11-р сар",
+            "12-р сар",
+        ],
+        [
+            "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ням",
+            "даваа",
+            "мягмар",
+            "лхагва",
+            "пүрэв",
+            "баасан",
+            "бямба",
+        ],
+        ["Ня", "Да", "Мя", "Лх", "Пү", "Ба", "Бя"],
+        ["Ня", "Да", "Мя", "Лх", "Пү", "Ба", "Бя"],
+        ["Ня", "Да", "Мя", "Лх", "Пү", "Ба", "Бя"],
+    ),
+    day_periods: Locale::new_day_periods(["ү.ө.", "ү.х."], ["ү.ө.", "ү.х."], ["ү.ө.", "ү.х."]),
+    eras: Locale::new_eras("МЭӨ", "МЭ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mn-Mong` locale.
+pub static MN_MONG: Locale<'static> = Locale {
+    lang: "mn-Mong",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mn-Mong-MN` locale.
+pub static MN_MONG_MN: Locale<'static> = Locale {
+    lang: "mn-Mong-MN",
+    dates: Locale::new_patterns(
+        "y ᠣᠨ ᠎᠎᠎ᠤ MMMM᠎᠎ᠢᠢᠨd. EEEE ᠋ᠭᠠᠷᠠᠭ",
+        "y ᠋ᠣᠨ ᠤMMMM᠎᠎ ᠤᠩ d",
+        "y.MM.dd",
+        "y.MM.dd",
+    ),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ᠨᠢᠭᠡᠳᠥᠭᠡᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠬᠣᠶᠠᠳᠣᠭᠠᠷ ᠰᠠᠷ ᠠ",
+            "ᠭᠣᠷᠪᠡᠳᠣᠭᠠᠷ ᠰᠠᠷ ᠠ",
+            "ᠳᠥᠷᠪᠡᠳᠥᠭᠡᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠲᠠᠪᠣᠳᠣᠭᠠᠷ ᠰᠠᠷ ᠠ",
+            "ᠵᠢᠷᠭᠣᠭᠠᠳᠣᠭᠠᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠲᠣᠯᠣᠭᠠᠳᠣᠭᠠᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠨᠠᠢᠮᠠᠳᠥᠭᠠᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠶᠢᠰᠥᠳᠥᠭᠡᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠠᠷᠪᠠᠳᠣᠭᠠᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠠᠷᠪᠠᠨ ᠨᠢᠭᠡᠳᠥᠭᠡᠷ ᠰᠠᠷ᠎ᠠ",
+            "ᠠᠷᠪᠠᠨ ᠬᠣᠶᠠᠳᠣᠭᠠᠷ ᠰᠠᠷ᠎ᠠ",
+        ],
+        [
+            "1 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "2 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "3᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "4 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "5 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "6 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "7 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "8᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "9 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "10 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "11 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+            "12 ᠊ᠷ ᠰᠠᠷ᠎ᠠ",
+        ],
+        [
+            "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ᠨᠢᠮ᠎ᠠ",
+            "ᠳᠠᠸᠠ",
+            "ᠮᠢᠠᠠᠮᠠᠷ",
+            "ᡀᠠᠭᠪᠠ",
+            "ᠫᠦᠷᠪᠦ",
+            "ᠪᠠᠰᠠᠩ",
+            "ᠪᠢᠮᠪᠠ",
+        ],
+        ["ᠨᠢ", "ᠲᠠ", "ᠮᠢᠭ", "ᡀᠠ", "ᠫᠥᠷ", "ᠪᠠ", "ᠪᠢᠮ"],
+        ["ᠨᠢ", "ᠲᠠ", "ᠮᠢᠭ", "ᡀᠠ", "ᠫᠥᠷ", "ᠪᠠ", "ᠪᠢᠮ"],
+        ["ᠨᠢ", "ᠳᠠ", "ᠮᠢᠭ", "ᡀᠠ", "ᠫᠥᠷ", "ᠪᠠ", "ᠪᠢ"],
+    ),
+    day_periods: Locale::new_day_periods(["ᠦ᠂ ᠥ", "ᠦ᠂ ᠬᠣ"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ᠮ᠂ ᠡᠡ᠂ ᠦ", "ᠮ᠂ ᠡ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mni` locale.
+pub static MNI: Locale<'static> = Locale {
+    lang: "mni",
+    dates: Locale::new_patterns("MMMM d, y, EEEE", "MMMM d, y", "MMM d, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "জনুৱারী",
+            "ফেব্রুৱারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "‌ওগষ্ট",
+            "সেপ্টেম্বর",
+            "ওক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জন",
+            "ফেব্রুৱারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "ওগ",
+            "সেপ্টেম্বর",
+            "ওক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জা",
+            "ফে",
+            "মার",
+            "এপ",
+            "মে",
+            "জুন",
+            "জুল",
+            "আ",
+            "সে",
+            "ওক",
+            "নব",
+            "ডি",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        ["নোং", "নিং", "লৈবা", "য়ুম", "শগো", "ইরা", "থাং"],
+    ),
+    day_periods: Locale::new_day_periods(["এ এম", "পি এম"], ["নুমাং", "PM"], ["নুমাং", "PM"]),
+    eras: Locale::new_eras("খৃ: মমাং", "খৃ: মতুং"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mni-Beng` locale.
+pub static MNI_BENG: Locale<'static> = Locale {
+    lang: "mni-Beng",
+    dates: Locale::new_patterns("MMMM d, y, EEEE", "MMMM d, y", "MMM d, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "জনুৱারী",
+            "ফেব্রুৱারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "‌ওগষ্ট",
+            "সেপ্টেম্বর",
+            "ওক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জন",
+            "ফেব্রুৱারি",
+            "মার্চ",
+            "এপ্রিল",
+            "মে",
+            "জুন",
+            "জুলাই",
+            "ওগ",
+            "সেপ্টেম্বর",
+            "ওক্টোবর",
+            "নভেম্বর",
+            "ডিসেম্বর",
+        ],
+        [
+            "জা",
+            "ফে",
+            "মার",
+            "এপ",
+            "মে",
+            "জুন",
+            "জুল",
+            "আ",
+            "সে",
+            "ওক",
+            "নব",
+            "ডি",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        [
+            "নোংমাইজিং",
+            "নিংথৌকাবা",
+            "লৈবাকপোকপা",
+            "য়ুমশকৈশা",
+            "শগোলশেন",
+            "ইরাই",
+            "থাংজ",
+        ],
+        ["নোং", "নিং", "লৈবা", "য়ুম", "শগো", "ইরা", "থাং"],
+    ),
+    day_periods: Locale::new_day_periods(["এ এম", "পি এম"], ["নুমাং", "PM"], ["নুমাং", "PM"]),
+    eras: Locale::new_eras("খৃ: মমাং", "খৃ: মতুং"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mni-Mtei` locale.
+pub static MNI_MTEI: Locale<'static> = Locale {
+    lang: "mni-Mtei",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `moh` locale.
+pub static MOH: Locale<'static> = Locale {
+    lang: "moh",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mr` locale.
+pub static MR: Locale<'static> = Locale {
+    lang: "mr",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जानेवारी",
+            "फेब्रुवारी",
+            "मार्च",
+            "एप्रिल",
+            "मे",
+            "जून",
+            "जुलै",
+            "ऑगस्ट",
+            "सप्टेंबर",
+            "ऑक्टोबर",
+            "नोव्हेंबर",
+            "डिसेंबर",
+        ],
+        [
+            "जाने",
+            "फेब्रु",
+            "मार्च",
+            "एप्रि",
+            "मे",
+            "जून",
+            "जुलै",
+            "ऑग",
+            "सप्टें",
+            "ऑक्टो",
+            "नोव्हें",
+            "डिसें",
+        ],
+        [
+            "जा", "फे", "मा", "ए", "मे", "जू", "जु", "ऑ", "स", "ऑ", "नो", "डि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रविवार",
+            "सोमवार",
+            "मंगळवार",
+            "बुधवार",
+            "गुरुवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        ["रवि", "सोम", "मंगळ", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("ई. स. पू.", "इ. स."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ms` locale.
+pub static MS: Locale<'static> = Locale {
+    lang: "ms",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Mac",
+            "April",
+            "Mei",
+            "Jun",
+            "Julai",
+            "Ogos",
+            "September",
+            "Oktober",
+            "November",
+            "Disember",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu",
+        ],
+        ["Ahd", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+        ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+        ["A", "I", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["PG", "PTG"], ["PG", "PTG"], ["PG", "PTG"]),
+    eras: Locale::new_eras("S.M.", "TM"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ms-Arab` locale.
+pub static MS_ARAB: Locale<'static> = Locale {
+    lang: "ms-Arab",
+    dates: Locale::new_patterns("EEEE، d MMMM y", "d MMMM y", "dd/MM/y", "d/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ms-Arab-BN` locale.
+pub static MS_ARAB_BN: Locale<'static> = Locale {
+    lang: "ms-Arab-BN",
+    dates: Locale::new_patterns("dd MMMM y", "d MMMM y", "dd/MM/y", "d/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ms-BN` locale.
+pub static MS_BN: Locale<'static> = Locale {
+    lang: "ms-BN",
+    dates: Locale::new_patterns("dd MMMM y", "d MMMM y", "d MMM y", "d/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Mac",
+            "April",
+            "Mei",
+            "Jun",
+            "Julai",
+            "Ogos",
+            "September",
+            "Oktober",
+            "November",
+            "Disember",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu",
+        ],
+        ["Ahd", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+        ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+        ["A", "I", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["PG", "PTG"], ["PG", "PTG"], ["PG", "PTG"]),
+    eras: Locale::new_eras("S.M.", "TM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ms-ID` locale.
+pub static MS_ID: Locale<'static> = Locale {
+    lang: "ms-ID",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Mac",
+            "April",
+            "Mei",
+            "Jun",
+            "Julai",
+            "Ogos",
+            "September",
+            "Oktober",
+            "November",
+            "Disember",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu",
+        ],
+        ["Ahd", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+        ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+        ["A", "I", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["PG", "PTG"], ["PG", "PTG"], ["PG", "PTG"]),
+    eras: Locale::new_eras("S.M.", "TM"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ms-SG` locale.
+pub static MS_SG: Locale<'static> = Locale {
+    lang: "ms-SG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Februari",
+            "Mac",
+            "April",
+            "Mei",
+            "Jun",
+            "Julai",
+            "Ogos",
+            "September",
+            "Oktober",
+            "November",
+            "Disember",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ogo", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ahad", "Isnin", "Selasa", "Rabu", "Khamis", "Jumaat", "Sabtu",
+        ],
+        ["Ahd", "Isn", "Sel", "Rab", "Kha", "Jum", "Sab"],
+        ["Ah", "Is", "Se", "Ra", "Kh", "Ju", "Sa"],
+        ["A", "I", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["PG", "PTG"], ["PG", "PTG"], ["PG", "PTG"]),
+    eras: Locale::new_eras("S.M.", "TM"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mt` locale.
+pub static MT: Locale<'static> = Locale {
+    lang: "mt",
+    dates: Locale::new_patterns(
+        "EEEE, d 'ta'’ MMMM y",
+        "d 'ta'’ MMMM y",
+        "dd MMM y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jannar",
+            "Frar",
+            "Marzu",
+            "April",
+            "Mejju",
+            "Ġunju",
+            "Lulju",
+            "Awwissu",
+            "Settembru",
+            "Ottubru",
+            "Novembru",
+            "Diċembru",
+        ],
+        [
+            "Jan", "Fra", "Mar", "Apr", "Mej", "Ġun", "Lul", "Aww", "Set", "Ott", "Nov", "Diċ",
+        ],
+        ["J", "F", "M", "A", "M", "Ġ", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Il-Ħadd",
+            "It-Tnejn",
+            "It-Tlieta",
+            "L-Erbgħa",
+            "Il-Ħamis",
+            "Il-Ġimgħa",
+            "Is-Sibt",
+        ],
+        ["Ħad", "Tne", "Tli", "Erb", "Ħam", "Ġim", "Sib"],
+        ["Ħad", "Tne", "Tli", "Erb", "Ħam", "Ġim", "Sib"],
+        ["Ħd", "T", "Tl", "Er", "Ħm", "Ġm", "Sb"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("QK", "WK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mua` locale.
+pub static MUA: Locale<'static> = Locale {
+    lang: "mua",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Fĩi Loo",
+            "Cokcwaklaŋne",
+            "Cokcwaklii",
+            "Fĩi Marfoo",
+            "Madǝǝuutǝbijaŋ",
+            "Mamǝŋgwãafahbii",
+            "Mamǝŋgwãalii",
+            "Madǝmbii",
+            "Fĩi Dǝɓlii",
+            "Fĩi Mundaŋ",
+            "Fĩi Gwahlle",
+            "Fĩi Yuru",
+        ],
+        [
+            "FLO", "CLA", "CKI", "FMF", "MAD", "MBI", "MLI", "MAM", "FDE", "FMU", "FGW", "FYU",
+        ],
+        ["O", "A", "I", "F", "D", "B", "L", "M", "E", "U", "W", "Y"],
+    ),
+    days: Locale::new_days(
+        [
+            "Com’yakke",
+            "Comlaaɗii",
+            "Comzyiiɗii",
+            "Comkolle",
+            "Comkaldǝɓlii",
+            "Comgaisuu",
+            "Comzyeɓsuu",
+        ],
+        ["Cya", "Cla", "Czi", "Cko", "Cka", "Cga", "Cze"],
+        ["Cya", "Cla", "Czi", "Cko", "Cka", "Cga", "Cze"],
+        ["Y", "L", "Z", "O", "A", "G", "E"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["comme", "lilli"],
+        ["comme", "lilli"],
+        ["comme", "lilli"],
+    ),
+    eras: Locale::new_eras("KK", "PK"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `mus` locale.
+pub static MUS: Locale<'static> = Locale {
+    lang: "mus",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Rvfo Cuse",
+            "Hotvle Hvse",
+            "Tasahcuce",
+            "Tasahce Rakko",
+            "Ke Hvse",
+            "Kvco Hvse",
+            "Hiyuce",
+            "Hiyo Rakko",
+            "Otowoskuce",
+            "Otowoskv Rakko",
+            "Ehole",
+            "Rvfo Rakko",
+        ],
+        [
+            "Rvfo Cuse",
+            "Hotvle Hvse",
+            "Tasahcuce",
+            "Tasahce Rakko",
+            "Ke Hvse",
+            "Kvco Hvse",
+            "Hiyuce",
+            "Hiyo Rakko",
+            "Otowoskuce",
+            "Otowoskv Rakko",
+            "Ehole",
+            "Rvfo Rakko",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Nettvʼcako",
+            "Enhvteceskv",
+            "Enhvteceskv Enhvyvtke",
+            "Ennvrkvpv",
+            "Ennvrkvpv Enhvyvtke",
+            "Nak Okkoskv Nettv",
+            "Nettv Cakʼcuse",
+        ],
+        [
+            "Nettvʼcako",
+            "Enhvteceskv",
+            "Enhvteceskv Enhvyvtke",
+            "Ennvrkvpv",
+            "Ennvrkvpv Enhvyvtke",
+            "Nak Okkoskv Nettv",
+            "Nettv Cakʼcuse",
+        ],
+        [
+            "Nettvʼcako",
+            "Enhvteceskv",
+            "Enhvteceskv Enhvyvtke",
+            "Ennvrkvpv",
+            "Ennvrkvpv Enhvyvtke",
+            "Nak Okkoskv Nettv",
+            "Nettv Cakʼcuse",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mww` locale.
+pub static MWW: Locale<'static> = Locale {
+    lang: "mww",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mww-Hmnp` locale.
+pub static MWW_HMNP: Locale<'static> = Locale {
+    lang: "mww-Hmnp",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `my` locale.
+pub static MY: Locale<'static> = Locale {
+    lang: "my",
+    dates: Locale::new_patterns("y MMMM d EEEE", "y MMMM d", "y MMM d", "d/M/yy"),
+    times: Locale::new_patterns("zzzz HH:mm:ss", "z HH:mm:ss", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ဇန်နဝါရီ",
+            "ဖေဖော်ဝါရီ",
+            "မတ်",
+            "ဧပြီ",
+            "မေ",
+            "ဇွန်",
+            "ဇူလိုင်",
+            "ဩဂုတ်",
+            "စက်တင်ဘာ",
+            "အောက်တိုဘာ",
+            "နိုဝင်ဘာ",
+            "ဒီဇင်ဘာ",
+        ],
+        [
+            "ဇန်",
+            "ဖေ",
+            "မတ်",
+            "ဧ",
+            "မေ",
+            "ဇွန်",
+            "ဇူ",
+            "ဩ",
+            "စက်",
+            "အောက်",
+            "နို",
+            "ဒီ",
+        ],
+        ["ဇ", "ဖ", "မ", "ဧ", "မ", "ဇ", "ဇ", "ဩ", "စ", "အ", "န", "ဒ"],
+    ),
+    days: Locale::new_days(
+        [
+            "တနင်္ဂနွေ",
+            "တနင်္လာ",
+            "အင်္ဂါ",
+            "ဗုဒ္ဓဟူး",
+            "ကြာသပတေး",
+            "သောကြာ",
+            "စနေ",
+        ],
+        [
+            "တနင်္ဂနွေ",
+            "တနင်္လာ",
+            "အင်္ဂါ",
+            "ဗုဒ္ဓဟူး",
+            "ကြာသပတေး",
+            "သောကြာ",
+            "စနေ",
+        ],
+        ["နွေ", "လာ", "ဂါ", "ဟူး", "တေး", "ကြာ", "နေ"],
+        ["တ", "တ", "အ", "ဗ", "က", "သ", "စ"],
+    ),
+    day_periods: Locale::new_day_periods(["နံနက်", "ညနေ"], ["နံနက်", "ညနေ"], ["နံနက်", "ညနေ"]),
+    eras: Locale::new_eras("ဘီစီ", "အဒေီ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `myv` locale.
+pub static MYV: Locale<'static> = Locale {
+    lang: "myv",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `mzn` locale.
+pub static MZN: Locale<'static> = Locale {
+    lang: "mzn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ژانویه",
+            "فوریه",
+            "مارس",
+            "آوریل",
+            "مه",
+            "ژوئن",
+            "ژوئیه",
+            "اوت",
+            "سپتامبر",
+            "اکتبر",
+            "نوامبر",
+            "دسامبر",
+        ],
+        [
+            "ژانویه",
+            "فوریه",
+            "مارس",
+            "آوریل",
+            "مه",
+            "ژوئن",
+            "ژوئیه",
+            "اوت",
+            "سپتامبر",
+            "اکتبر",
+            "نوامبر",
+            "دسامبر",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("پ.م", "م."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `naq` locale.
+pub static NAQ: Locale<'static> = Locale {
+    lang: "naq",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ǃKhanni",
+            "ǃKhanǀgôab",
+            "ǀKhuuǁkhâb",
+            "ǃHôaǂkhaib",
+            "ǃKhaitsâb",
+            "Gamaǀaeb",
+            "ǂKhoesaob",
+            "Aoǁkhuumûǁkhâb",
+            "Taraǀkhuumûǁkhâb",
+            "ǂNûǁnâiseb",
+            "ǀHooǂgaeb",
+            "Hôasoreǁkhâb",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sontaxtsees",
+            "Mantaxtsees",
+            "Denstaxtsees",
+            "Wunstaxtsees",
+            "Dondertaxtsees",
+            "Fraitaxtsees",
+            "Satertaxtsees",
+        ],
+        ["Son", "Ma", "De", "Wu", "Do", "Fr", "Sat"],
+        ["Son", "Ma", "De", "Wu", "Do", "Fr", "Sat"],
+        ["S", "M", "E", "W", "D", "F", "A"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ǁgoagas", "ǃuias"],
+        ["ǁgoagas", "ǃuias"],
+        ["ǁgoagas", "ǃuias"],
+    ),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nb` locale.
+pub static NB: Locale<'static> = Locale {
+    lang: "nb",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "april",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "mai", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag",
+        ],
+        ["søn.", "man.", "tir.", "ons.", "tor.", "fre.", "lør."],
+        ["sø.", "ma.", "ti.", "on.", "to.", "fr.", "lø."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nb-SJ` locale.
+pub static NB_SJ: Locale<'static> = Locale {
+    lang: "nb-SJ",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "april",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "mai", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag",
+        ],
+        ["søn.", "man.", "tir.", "ons.", "tor.", "fre.", "lør."],
+        ["sø.", "ma.", "ti.", "on.", "to.", "fr.", "lø."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nd` locale.
+pub static ND: Locale<'static> = Locale {
+    lang: "nd",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Zibandlela",
+            "Nhlolanja",
+            "Mbimbitho",
+            "Mabasa",
+            "Nkwenkwezi",
+            "Nhlangula",
+            "Ntulikazi",
+            "Ncwabakazi",
+            "Mpandula",
+            "Mfumfu",
+            "Lwezi",
+            "Mpalakazi",
+        ],
+        [
+            "Zib", "Nhlo", "Mbi", "Mab", "Nkw", "Nhla", "Ntu", "Ncw", "Mpan", "Mfu", "Lwe", "Mpal",
+        ],
+        ["Z", "N", "M", "M", "N", "N", "N", "N", "M", "M", "L", "M"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonto", "Mvulo", "Sibili", "Sithathu", "Sine", "Sihlanu", "Mgqibelo",
+        ],
+        ["Son", "Mvu", "Sib", "Sit", "Sin", "Sih", "Mgq"],
+        ["Son", "Mvu", "Sib", "Sit", "Sin", "Sih", "Mgq"],
+        ["S", "M", "S", "S", "S", "S", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nds` locale.
+pub static NDS: Locale<'static> = Locale {
+    lang: "nds",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januaar",
+            "Februaar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktover",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Januaar",
+            "Februaar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktover",
+            "November",
+            "Dezember",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nds-NL` locale.
+pub static NDS_NL: Locale<'static> = Locale {
+    lang: "nds-NL",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januaar",
+            "Februaar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktover",
+            "November",
+            "Dezember",
+        ],
+        [
+            "Januaar",
+            "Februaar",
+            "März",
+            "April",
+            "Mai",
+            "Juni",
+            "Juli",
+            "August",
+            "September",
+            "Oktover",
+            "November",
+            "Dezember",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        [
+            "Sünndag",
+            "Maandag",
+            "Dingsdag",
+            "Middeweken",
+            "Dunnersdag",
+            "Freedag",
+            "Sünnavend",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ne` locale.
+pub static NE: Locale<'static> = Locale {
+    lang: "ne",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "yy/M/d"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फेब्रुअरी",
+            "मार्च",
+            "अप्रिल",
+            "मे",
+            "जुन",
+            "जुलाई",
+            "अगस्ट",
+            "सेप्टेम्बर",
+            "अक्टोबर",
+            "नोभेम्बर",
+            "डिसेम्बर",
+        ],
+        [
+            "जनवरी",
+            "फेब्रुअरी",
+            "मार्च",
+            "अप्रिल",
+            "मे",
+            "जुन",
+            "जुलाई",
+            "अगस्ट",
+            "सेप्टेम्बर",
+            "अक्टोबर",
+            "नोभेम्बर",
+            "डिसेम्बर",
+        ],
+        [
+            "जन",
+            "फेब",
+            "मार्च",
+            "अप्र",
+            "मे",
+            "जुन",
+            "जुल",
+            "अग",
+            "सेप",
+            "अक्टो",
+            "नोभे",
+            "डिसे",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "आइतबार",
+            "सोमबार",
+            "मङ्गलबार",
+            "बुधबार",
+            "बिहिबार",
+            "शुक्रबार",
+            "शनिबार",
+        ],
+        ["आइत", "सोम", "मङ्गल", "बुध", "बिहि", "शुक्र", "शनि"],
+        ["आइत", "सोम", "मङ्गल", "बुध", "बिहि", "शुक्र", "शनि"],
+        ["आ", "सो", "म", "बु", "बि", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["पूर्वाह्न", "अपराह्न"],
+        ["पूर्वाह्न", "अपराह्न"],
+        ["पूर्वाह्न", "अपराह्न"],
+    ),
+    eras: Locale::new_eras("ईसा पूर्व", "सन्"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ne-IN` locale.
+pub static NE_IN: Locale<'static> = Locale {
+    lang: "ne-IN",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "yy/M/d"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फेब्रुअरी",
+            "मार्च",
+            "अप्रिल",
+            "मे",
+            "जुन",
+            "जुलाई",
+            "अगस्ट",
+            "सेप्टेम्बर",
+            "अक्टोबर",
+            "नोभेम्बर",
+            "डिसेम्बर",
+        ],
+        [
+            "जनवरी",
+            "फेब्रुअरी",
+            "मार्च",
+            "अप्रिल",
+            "मे",
+            "जुन",
+            "जुलाई",
+            "अगस्ट",
+            "सेप्टेम्बर",
+            "अक्टोबर",
+            "नोभेम्बर",
+            "डिसेम्बर",
+        ],
+        [
+            "जन",
+            "फेब",
+            "मार्च",
+            "अप्र",
+            "मे",
+            "जुन",
+            "जुल",
+            "अग",
+            "सेप",
+            "अक्टो",
+            "नोभे",
+            "डिसे",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "आइतबार",
+            "सोमबार",
+            "मङ्गलबार",
+            "बुधबार",
+            "बिहिबार",
+            "शुक्रबार",
+            "शनिबार",
+        ],
+        ["आइत", "सोम", "मङ्गल", "बुध", "बिहि", "शुक्र", "शनि"],
+        ["आइत", "सोम", "मङ्गल", "बुध", "बिहि", "शुक्र", "शनि"],
+        ["आ", "सो", "म", "बु", "बि", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["पूर्वाह्न", "अपराह्न"],
+        ["पूर्वाह्न", "अपराह्न"],
+        ["पूर्वाह्न", "अपराह्न"],
+    ),
+    eras: Locale::new_eras("ईसा पूर्व", "सन्"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nl` locale.
+pub static NL: Locale<'static> = Locale {
+    lang: "nl",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-AW` locale.
+pub static NL_AW: Locale<'static> = Locale {
+    lang: "nl-AW",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-BE` locale.
+pub static NL_BE: Locale<'static> = Locale {
+    lang: "nl-BE",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-BQ` locale.
+pub static NL_BQ: Locale<'static> = Locale {
+    lang: "nl-BQ",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-CW` locale.
+pub static NL_CW: Locale<'static> = Locale {
+    lang: "nl-CW",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-SR` locale.
+pub static NL_SR: Locale<'static> = Locale {
+    lang: "nl-SR",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nl-SX` locale.
+pub static NL_SX: Locale<'static> = Locale {
+    lang: "nl-SX",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "maart",
+            "april",
+            "mei",
+            "juni",
+            "juli",
+            "augustus",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "zondag",
+            "maandag",
+            "dinsdag",
+            "woensdag",
+            "donderdag",
+            "vrijdag",
+            "zaterdag",
+        ],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["zo", "ma", "di", "wo", "do", "vr", "za"],
+        ["Z", "M", "D", "W", "D", "V", "Z"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("v.Chr.", "n.Chr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `nmg` locale.
+pub static NMG: Locale<'static> = Locale {
+    lang: "nmg",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ngwɛn matáhra",
+            "ngwɛn ńmba",
+            "ngwɛn ńlal",
+            "ngwɛn ńna",
+            "ngwɛn ńtan",
+            "ngwɛn ńtuó",
+            "ngwɛn hɛmbuɛrí",
+            "ngwɛn lɔmbi",
+            "ngwɛn rɛbvuâ",
+            "ngwɛn wum",
+            "ngwɛn wum navǔr",
+            "krísimin",
+        ],
+        [
+            "ng1", "ng2", "ng3", "ng4", "ng5", "ng6", "ng7", "ng8", "ng9", "ng10", "ng11", "kris",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "sɔ́ndɔ",
+            "mɔ́ndɔ",
+            "sɔ́ndɔ mafú mába",
+            "sɔ́ndɔ mafú málal",
+            "sɔ́ndɔ mafú mána",
+            "mabágá má sukul",
+            "sásadi",
+        ],
+        ["sɔ́n", "mɔ́n", "smb", "sml", "smn", "mbs", "sas"],
+        ["sɔ́n", "mɔ́n", "smb", "sml", "smn", "mbs", "sas"],
+        ["s", "m", "s", "s", "s", "m", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["maná", "kugú"], ["maná", "kugú"], ["maná", "kugú"]),
+    eras: Locale::new_eras("BL", "PB"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nn` locale.
+pub static NN: Locale<'static> = Locale {
+    lang: "nn",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} 'kl'. {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "april",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "mai", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "måndag", "tysdag", "onsdag", "torsdag", "fredag", "laurdag",
+        ],
+        ["sø.", "må.", "ty.", "on.", "to.", "fr.", "la."],
+        ["sø.", "må.", "ty.", "on.", "to.", "fr.", "la."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["f.m.", "e.m."], ["f.m.", "e.m."], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nnh` locale.
+pub static NNH: Locale<'static> = Locale {
+    lang: "nnh",
+    dates: Locale::new_patterns(
+        "EEEE , 'lyɛ'̌ʼ d 'na' MMMM, y",
+        "'lyɛ'̌ʼ d 'na' MMMM, y",
+        "d MMM, y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1},{0}", "{1}, {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "saŋ tsetsɛ̀ɛ lùm",
+            "saŋ kàg ngwóŋ",
+            "saŋ lepyè shúm",
+            "saŋ cÿó",
+            "saŋ tsɛ̀ɛ cÿó",
+            "saŋ njÿoláʼ",
+            "saŋ tyɛ̀b tyɛ̀b mbʉ̀ŋ",
+            "saŋ mbʉ̀ŋ",
+            "saŋ ngwɔ̀ʼ mbÿɛ",
+            "saŋ tàŋa tsetsáʼ",
+            "saŋ mejwoŋó",
+            "saŋ lùm",
+        ],
+        [
+            "saŋ tsetsɛ̀ɛ lùm",
+            "saŋ kàg ngwóŋ",
+            "saŋ lepyè shúm",
+            "saŋ cÿó",
+            "saŋ tsɛ̀ɛ cÿó",
+            "saŋ njÿoláʼ",
+            "saŋ tyɛ̀b tyɛ̀b mbʉ̀ŋ",
+            "saŋ mbʉ̀ŋ",
+            "saŋ ngwɔ̀ʼ mbÿɛ",
+            "saŋ tàŋa tsetsáʼ",
+            "saŋ mejwoŋó",
+            "saŋ lùm",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "lyɛʼɛ́ sẅíŋtè",
+            "mvfò lyɛ̌ʼ",
+            "mbɔ́ɔntè mvfò lyɛ̌ʼ",
+            "tsètsɛ̀ɛ lyɛ̌ʼ",
+            "mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ",
+            "mvfò màga lyɛ̌ʼ",
+            "màga lyɛ̌ʼ",
+        ],
+        [
+            "lyɛʼɛ́ sẅíŋtè",
+            "mvfò lyɛ̌ʼ",
+            "mbɔ́ɔntè mvfò lyɛ̌ʼ",
+            "tsètsɛ̀ɛ lyɛ̌ʼ",
+            "mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ",
+            "mvfò màga lyɛ̌ʼ",
+            "màga lyɛ̌ʼ",
+        ],
+        [
+            "lyɛʼɛ́ sẅíŋtè",
+            "mvfò lyɛ̌ʼ",
+            "mbɔ́ɔntè mvfò lyɛ̌ʼ",
+            "tsètsɛ̀ɛ lyɛ̌ʼ",
+            "mbɔ́ɔntè tsetsɛ̀ɛ lyɛ̌ʼ",
+            "mvfò màga lyɛ̌ʼ",
+            "màga lyɛ̌ʼ",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["mbaʼámbaʼ", "ncwònzém"],
+        ["mbaʼámbaʼ", "ncwònzém"],
+        ["mbaʼámbaʼ", "ncwònzém"],
+    ),
+    eras: Locale::new_eras("m.z.Y.", "m.g.n.Y."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `no` locale.
+pub static NO: Locale<'static> = Locale {
+    lang: "no",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mars",
+            "april",
+            "mai",
+            "juni",
+            "juli",
+            "august",
+            "september",
+            "oktober",
+            "november",
+            "desember",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "mai", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "des.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "søndag", "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag",
+        ],
+        ["søn.", "man.", "tir.", "ons.", "tor.", "fre.", "lør."],
+        ["sø.", "ma.", "ti.", "on.", "to.", "fr.", "lø."],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a", "p"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nqo` locale.
+pub static NQO: Locale<'static> = Locale {
+    lang: "nqo",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ߓߌ߲ߠߊߥߎߟߋ߲",
+            "ߞߏ߲ߞߏߜߍ",
+            "ߕߙߊߓߊ",
+            "ߞߏ߲ߞߏߘߌ߬ߓߌ",
+            "ߘߓߊ߬ߕߊ",
+            "ߥߊ߬ߛߌ߬ߥߙߊ",
+            "ߞߊ߬ߙߌߝߐ߭",
+            "ߘߓߊ߬ߓߌߟߊ",
+            "ߕߎߟߊߝߌ߲",
+            "ߞߏ߲ߓߌߕߌ߮",
+            "ߣߍߣߍߓߊ",
+            "ߞߏߟߌ߲ߞߏߟߌ߲",
+        ],
+        [
+            "ߓߌ߲ߠ",
+            "ߞߏ߲ߞ",
+            "ߕߙߊ",
+            "ߞߏ߲ߘ",
+            "ߘߓߊ߬ߕ",
+            "ߥߊ߬ߛ",
+            "ߞߊ߬ߙ",
+            "ߘߓߊ߬ߓ",
+            "ߕߎߟߊߝߌ߲",
+            "ߞߏ߲ߓ",
+            "ߣߍߣ",
+            "ߞߏߟ",
+        ],
+        ["ߓ", "ߞ", "ߕ", "ߞ", "ߘ", "ߥ", "ߞ", "ߘ", "ߕ", "ߞ", "ߣ", "ߞ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ߞߊ߯ߙߌߟߏ߲",
+            "ߞߐ߬ߓߊ߬ߟߏ߲",
+            "ߞߐ߬ߟߏ߲",
+            "ߞߎߣߎ߲ߟߏ߲",
+            "ߓߌߟߏ߲",
+            "ߛߌ߬ߣߌ߲߬ߟߏ߲",
+            "ߞߍ߲ߘߍߟߏ߲",
+        ],
+        ["ߞߊ߯ߙ", "ߞߐ߬ߓ", "ߞߐ߬ߟߏ߲", "ߞߎߣ", "ߓߌߟ", "ߛߌ߬ߣ", "ߞߍ߲ߘ"],
+        ["ߞߊ߯", "ߞߐ߬", "ߞߐ߬ߟߏ߲", "ߞߎ", "ߓߌ", "ߛߌ߬", "ߞߍ߲"],
+        ["ߞ", "ߞ", "ߞ", "ߞ", "ߓ", "ߛ", "ߞ"],
+    ),
+    day_periods: Locale::new_day_periods(["ߛ", "ߥ"], ["ߛ", "ߥ"], ["ߛ", "ߥ"]),
+    eras: Locale::new_eras("ߌߛ. ߡ. ߢߍ߫", "ߌߛ. ߡ. ߞߐ߫"),
+    decimal: '.',
+    group: '،',
+};
+/// Data for `nr` locale.
+pub static NR: Locale<'static> = Locale {
+    lang: "nr",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `nso` locale.
+pub static NSO: Locale<'static> = Locale {
+    lang: "nso",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} 'ka' {0}", "{1} 'ka' {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Janeware",
+            "Febereware",
+            "Matšhe",
+            "Aporele",
+            "Mei",
+            "June",
+            "Julae",
+            "Agosetose",
+            "Setemere",
+            "Oktobore",
+            "Nofemere",
+            "Disemere",
+        ],
+        [
+            "Jan", "Feb", "Mat", "Apo", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nof", "Dis",
+        ],
+        ["P", "D", "H", "M", "M", "P", "M", "P", "L", "D", "D", "M"],
+    ),
+    days: Locale::new_days(
+        [
+            "Lamorena",
+            "Mošupologo",
+            "Labobedi",
+            "Laboraro",
+            "Labone",
+            "Labohlano",
+            "Mokibelo",
+        ],
+        ["Lam", "Mos", "Bed", "Rar", "Ne", "Hla", "Mok"],
+        ["Lam", "Mos", "Bed", "Rar", "Ne", "Hla", "Mok"],
+        ["L", "M", "B", "R", "N", "H", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nus` locale.
+pub static NUS: Locale<'static> = Locale {
+    lang: "nus",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/MM/y"),
+    times: Locale::new_patterns("zzzz h:mm:ss a", "z h:mm:ss a", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Tiop thar pɛt",
+            "Pɛt",
+            "Duɔ̱ɔ̱ŋ",
+            "Guak",
+            "Duät",
+            "Kornyoot",
+            "Pay yie̱tni",
+            "Tho̱o̱r",
+            "Tɛɛr",
+            "Laath",
+            "Kur",
+            "Tio̱p in di̱i̱t",
+        ],
+        [
+            "Tiop",
+            "Pɛt",
+            "Duɔ̱ɔ̱",
+            "Guak",
+            "Duä",
+            "Kor",
+            "Pay",
+            "Thoo",
+            "Tɛɛ",
+            "Laa",
+            "Kur",
+            "Tid",
+        ],
+        ["T", "P", "D", "G", "D", "K", "P", "T", "T", "L", "K", "T"],
+    ),
+    days: Locale::new_days(
+        [
+            "Cäŋ kuɔth",
+            "Jiec la̱t",
+            "Rɛw lätni",
+            "Diɔ̱k lätni",
+            "Ŋuaan lätni",
+            "Dhieec lätni",
+            "Bäkɛl lätni",
+        ],
+        ["Cäŋ", "Jiec", "Rɛw", "Diɔ̱k", "Ŋuaan", "Dhieec", "Bäkɛl"],
+        ["Cäŋ", "Jiec", "Rɛw", "Diɔ̱k", "Ŋuaan", "Dhieec", "Bäkɛl"],
+        ["C", "J", "R", "D", "Ŋ", "D", "B"],
+    ),
+    day_periods: Locale::new_day_periods(["RW", "TŊ"], ["RW", "TŊ"], ["RW", "TŊ"]),
+    eras: Locale::new_eras("AY", "ƐY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nv` locale.
+pub static NV: Locale<'static> = Locale {
+    lang: "nv",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ny` locale.
+pub static NY: Locale<'static> = Locale {
+    lang: "ny",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `nyn` locale.
+pub static NYN: Locale<'static> = Locale {
+    lang: "nyn",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Okwokubanza",
+            "Okwakabiri",
+            "Okwakashatu",
+            "Okwakana",
+            "Okwakataana",
+            "Okwamukaaga",
+            "Okwamushanju",
+            "Okwamunaana",
+            "Okwamwenda",
+            "Okwaikumi",
+            "Okwaikumi na kumwe",
+            "Okwaikumi na ibiri",
+        ],
+        [
+            "KBZ", "KBR", "KST", "KKN", "KTN", "KMK", "KMS", "KMN", "KMW", "KKM", "KNK", "KNB",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sande",
+            "Orwokubanza",
+            "Orwakabiri",
+            "Orwakashatu",
+            "Orwakana",
+            "Orwakataano",
+            "Orwamukaaga",
+        ],
+        ["SAN", "ORK", "OKB", "OKS", "OKN", "OKT", "OMK"],
+        ["SAN", "ORK", "OKB", "OKS", "OKN", "OKT", "OMK"],
+        ["S", "K", "R", "S", "N", "T", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `oc` locale.
+pub static OC: Locale<'static> = Locale {
+    lang: "oc",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de genièr",
+            "de febrièr",
+            "de març",
+            "d’abril",
+            "de mai",
+            "de junh",
+            "de julhet",
+            "d’agost",
+            "de setembre",
+            "d’octòbre",
+            "de novembre",
+            "de decembre",
+        ],
+        [
+            "de genièr",
+            "de febrièr",
+            "de març",
+            "d’abril",
+            "de mai",
+            "de junh",
+            "de julhet",
+            "d’agost",
+            "de setembre",
+            "d’octòbre",
+            "de novembre",
+            "de decembre",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `oc-ES` locale.
+pub static OC_ES: Locale<'static> = Locale {
+    lang: "oc-ES",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "de genièr",
+            "de febrièr",
+            "de març",
+            "d’abril",
+            "de mai",
+            "de junh",
+            "de julhet",
+            "d’agost",
+            "de setembre",
+            "d’octòbre",
+            "de novembre",
+            "de decembre",
+        ],
+        [
+            "de genièr",
+            "de febrièr",
+            "de març",
+            "d’abril",
+            "de mai",
+            "de junh",
+            "de julhet",
+            "d’agost",
+            "de setembre",
+            "d’octòbre",
+            "de novembre",
+            "de decembre",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        [
+            "dimenge",
+            "diluns",
+            "dimars",
+            "dimècres",
+            "dijòus",
+            "divendres",
+            "dissabte",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `oka` locale.
+pub static OKA: Locale<'static> = Locale {
+    lang: "oka",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `oka-US` locale.
+pub static OKA_US: Locale<'static> = Locale {
+    lang: "oka-US",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `om` locale.
+pub static OM: Locale<'static> = Locale {
+    lang: "om",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Amajjii",
+            "Guraandhala",
+            "Bitootessa",
+            "Eebila",
+            "Caamsaa",
+            "Waxabajjii",
+            "Adoolessa",
+            "Hagayya",
+            "Fulbaana",
+            "Onkoloolessa",
+            "Sadaasa",
+            "Mudde",
+        ],
+        [
+            "Ama",
+            "Gur",
+            "Bitootessa",
+            "Elb",
+            "Cam",
+            "Wax",
+            "Ado",
+            "Hag",
+            "Ful",
+            "Onk",
+            "Sadaasa",
+            "Mud",
+        ],
+        ["A", "G", "B", "E", "C", "W", "A", "H", "F", "O", "S", "M"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dilbata", "Wiixata", "Kibxata", "Roobii", "Kamisa", "Jimaata", "Sanbata",
+        ],
+        ["Dil", "Wix", "Kib", "Rob", "Kam", "Jim", "San"],
+        ["Dil", "Wix", "Kib", "Rob", "Kam", "Jim", "San"],
+        ["D", "W", "K", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["WD", "WB"], ["WD", "WB"], ["WD", "WB"]),
+    eras: Locale::new_eras("DhKD", "BA"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `om-KE` locale.
+pub static OM_KE: Locale<'static> = Locale {
+    lang: "om-KE",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Amajjii",
+            "Guraandhala",
+            "Bitootessa",
+            "Eebila",
+            "Caamsaa",
+            "Waxabajjii",
+            "Adoolessa",
+            "Hagayya",
+            "Fulbaana",
+            "Onkoloolessa",
+            "Sadaasa",
+            "Mudde",
+        ],
+        [
+            "Ama",
+            "Gur",
+            "Bitootessa",
+            "Elb",
+            "Cam",
+            "Wax",
+            "Ado",
+            "Hag",
+            "Ful",
+            "Onk",
+            "Sadaasa",
+            "Mud",
+        ],
+        ["A", "G", "B", "E", "C", "W", "A", "H", "F", "O", "S", "M"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dilbata", "Wiixata", "Kibxata", "Roobii", "Kamisa", "Jimaata", "Sanbata",
+        ],
+        ["Dil", "Wix", "Kib", "Rob", "Kam", "Jim", "San"],
+        ["Dil", "Wix", "Kib", "Rob", "Kam", "Jim", "San"],
+        ["D", "W", "Q", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["WD", "WB"], ["WD", "WB"], ["WD", "WB"]),
+    eras: Locale::new_eras("KD", "BA"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `or` locale.
+pub static OR: Locale<'static> = Locale {
+    lang: "or",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ଜାନୁଆରୀ",
+            "ଫେବୃଆରୀ",
+            "ମାର୍ଚ୍ଚ",
+            "ଅପ୍ରେଲ",
+            "ମଇ",
+            "ଜୁନ",
+            "ଜୁଲାଇ",
+            "ଅଗଷ୍ଟ",
+            "ସେପ୍ଟେମ୍ବର",
+            "ଅକ୍ଟୋବର",
+            "ନଭେମ୍ବର",
+            "ଡିସେମ୍ବର",
+        ],
+        [
+            "ଜାନୁଆରୀ",
+            "ଫେବୃଆରୀ",
+            "ମାର୍ଚ୍ଚ",
+            "ଅପ୍ରେଲ",
+            "ମଇ",
+            "ଜୁନ",
+            "ଜୁଲାଇ",
+            "ଅଗଷ୍ଟ",
+            "ସେପ୍ଟେମ୍ବର",
+            "ଅକ୍ଟୋବର",
+            "ନଭେମ୍ବର",
+            "ଡିସେମ୍ବର",
+        ],
+        [
+            "ଜା", "ଫେ", "ମା", "ଅ", "ମଇ", "ଜୁ", "ଜୁ", "ଅ", "ସେ", "ଅ", "ନ", "ଡି",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ରବିବାର", "ସୋମବାର", "ମଙ୍ଗଳବାର", "ବୁଧବାର", "ଗୁରୁବାର", "ଶୁକ୍ରବାର", "ଶନିବାର"],
+        ["ରବି", "ସୋମ", "ମଙ୍ଗଳ", "ବୁଧ", "ଗୁରୁ", "ଶୁକ୍ର", "ଶନି"],
+        ["ରବି", "ସୋମ", "ମଙ୍ଗଳ", "ବୁଧ", "ଗୁରୁ", "ଶୁକ୍ର", "ଶନି"],
+        ["ର", "ସୋ", "ମ", "ବୁ", "ଗୁ", "ଶୁ", "ଶ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `os` locale.
+pub static OS: Locale<'static> = Locale {
+    lang: "os",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM, y 'аз'",
+        "d MMMM, y 'аз'",
+        "dd MMM y 'аз'",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "январы",
+            "февралы",
+            "мартъийы",
+            "апрелы",
+            "майы",
+            "июны",
+            "июлы",
+            "августы",
+            "сентябры",
+            "октябры",
+            "ноябры",
+            "декабры",
+        ],
+        [
+            "янв.", "фев.", "мар.", "апр.", "майы", "июны", "июлы", "авг.", "сен.", "окт.", "ноя.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "хуыцаубон",
+            "къуырисӕр",
+            "дыццӕг",
+            "ӕртыццӕг",
+            "цыппӕрӕм",
+            "майрӕмбон",
+            "сабат",
+        ],
+        ["хцб", "крс", "дцг", "ӕрт", "цпр", "мрб", "сбт"],
+        ["хцб", "крс", "дцг", "ӕрт", "цпр", "мрб", "сбт"],
+        ["Х", "К", "Д", "Ӕ", "Ц", "М", "С"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ӕмбисбоны размӕ", "ӕмбисбоны фӕстӕ"],
+        ["AM", "PM"],
+        ["AM", "PM"],
+    ),
+    eras: Locale::new_eras("н.д.а.", "н.д."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `os-RU` locale.
+pub static OS_RU: Locale<'static> = Locale {
+    lang: "os-RU",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM, y 'аз'",
+        "d MMMM, y 'аз'",
+        "dd MMM y 'аз'",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "январы",
+            "февралы",
+            "мартъийы",
+            "апрелы",
+            "майы",
+            "июны",
+            "июлы",
+            "августы",
+            "сентябры",
+            "октябры",
+            "ноябры",
+            "декабры",
+        ],
+        [
+            "янв.", "фев.", "мар.", "апр.", "майы", "июны", "июлы", "авг.", "сен.", "окт.", "ноя.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "хуыцаубон",
+            "къуырисӕр",
+            "дыццӕг",
+            "ӕртыццӕг",
+            "цыппӕрӕм",
+            "майрӕмбон",
+            "сабат",
+        ],
+        ["хцб", "крс", "дцг", "ӕрт", "цпр", "мрб", "сбт"],
+        ["хцб", "крс", "дцг", "ӕрт", "цпр", "мрб", "сбт"],
+        ["Х", "К", "Д", "Ӕ", "Ц", "М", "С"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ӕмбисбоны размӕ", "ӕмбисбоны фӕстӕ"],
+        ["AM", "PM"],
+        ["AM", "PM"],
+    ),
+    eras: Locale::new_eras("н.д.а.", "н.д."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `osa` locale.
+pub static OSA: Locale<'static> = Locale {
+    lang: "osa",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓄𐓘𐓡𐓛͘𐓧𐓟",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓏𐓟𐓵𐓪͘𐓬𐓘",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓏𐓟𐓵𐓘𐓜𐓣",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓏𐓟𐓰𐓪𐓬𐓘",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓏𐓟𐓮𐓘𐓰𐓘",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓏𐓟𐓯𐓘𐓬𐓟",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐓄𐓟𐓵𐓪͘𐓬𐓘",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐒼𐓣𐓟𐓰𐓪𐓬𐓘",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐒿𐓟𐓜𐓛𐓲𐓟𐓷𐓣͘𐓤𐓟",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐒿𐓟𐓜𐓛",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐒰𐓧𐓣 𐓏𐓣͘𐓸𐓲𐓣",
+            "𐓀𐓣͘𐓪͘𐓬𐓘 𐒰𐓧𐓣 𐓍𐓪͘𐓬𐓘",
+        ],
+        [
+            "𐓄𐓘𐓡𐓛͘𐓧𐓟",
+            "𐓵𐓪͘𐓬𐓘",
+            "𐓵𐓘𐓜𐓣",
+            "𐓰𐓪𐓬𐓘",
+            "𐓮𐓘𐓰𐓘",
+            "𐓯𐓘𐓬𐓟",
+            "𐓄𐓟𐓵𐓪͘𐓬𐓘",
+            "𐒼𐓣𐓟𐓰𐓪𐓬𐓘",
+            "𐒿𐓟𐓜𐓛𐓲𐓟𐓷𐓣͘𐓤𐓟",
+            "𐒿𐓟𐓜𐓛",
+            "𐒰𐓧𐓣 𐓏𐓣͘𐓸𐓲𐓣",
+            "𐒰𐓧𐓣 𐓍𐓪͘𐓬𐓘",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓘𐓤𐓘͘𐓰𐓘𐓤𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓄𐓘𐓡𐓛͘𐓧𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓪͘𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓘𐓜𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓰𐓪𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓈𐓘 𐓵𐓘𐓲𐓘 𐓻𐓣͘",
+            "𐒹𐓘͘𐓬𐓘 𐓂𐓤𐓘𐓸𐓟 𐓣͘𐓤𐓟",
+        ],
+        [
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓘𐓤𐓘͘𐓰𐓘𐓤𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓄𐓘𐓡𐓛͘𐓧𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓪͘𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓘𐓜𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓰𐓪𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓈𐓘 𐓵𐓘𐓲𐓘 𐓻𐓣͘",
+            "𐒹𐓘͘𐓬𐓘 𐓂𐓤𐓘𐓸𐓟 𐓣͘𐓤𐓟",
+        ],
+        [
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓘𐓤𐓘͘𐓰𐓘𐓤𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓄𐓘𐓡𐓛͘𐓧𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓪͘𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓵𐓘𐓜𐓣",
+            "𐒹𐓘͘𐓬𐓘 𐓏𐓟𐓰𐓪𐓬𐓘",
+            "𐒹𐓘͘𐓬𐓘 𐓈𐓘 𐓵𐓘𐓲𐓘 𐓻𐓣͘",
+            "𐒹𐓘͘𐓬𐓘 𐓂𐓤𐓘𐓸𐓟 𐓣͘𐓤𐓟",
+        ],
+        ["𐓏", "𐓄", "𐓍", "𐒴", "𐓈", "𐓊", "𐓸"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pa` locale.
+pub static PA: Locale<'static> = Locale {
+    lang: "pa",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ਜਨਵਰੀ",
+            "ਫ਼ਰਵਰੀ",
+            "ਮਾਰਚ",
+            "ਅਪ੍ਰੈਲ",
+            "ਮਈ",
+            "ਜੂਨ",
+            "ਜੁਲਾਈ",
+            "ਅਗਸਤ",
+            "ਸਤੰਬਰ",
+            "ਅਕਤੂਬਰ",
+            "ਨਵੰਬਰ",
+            "ਦਸੰਬਰ",
+        ],
+        [
+            "ਜਨ",
+            "ਫ਼ਰ",
+            "ਮਾਰਚ",
+            "ਅਪ੍ਰੈ",
+            "ਮਈ",
+            "ਜੂਨ",
+            "ਜੁਲਾ",
+            "ਅਗ",
+            "ਸਤੰ",
+            "ਅਕਤੂ",
+            "ਨਵੰ",
+            "ਦਸੰ",
+        ],
+        ["ਜ", "ਫ਼", "ਮਾ", "ਅ", "ਮ", "ਜੂ", "ਜੁ", "ਅ", "ਸ", "ਅ", "ਨ", "ਦ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ਐਤਵਾਰ",
+            "ਸੋਮਵਾਰ",
+            "ਮੰਗਲਵਾਰ",
+            "ਬੁੱਧਵਾਰ",
+            "ਵੀਰਵਾਰ",
+            "ਸ਼ੁੱਕਰਵਾਰ",
+            "ਸ਼ਨੀਵਾਰ",
+        ],
+        ["ਐਤ", "ਸੋਮ", "ਮੰਗਲ", "ਬੁੱਧ", "ਵੀਰ", "ਸ਼ੁੱਕਰ", "ਸ਼ਨੀ"],
+        ["ਐਤ", "ਸੋਮ", "ਮੰਗ", "ਬੁੱਧ", "ਵੀਰ", "ਸ਼ੁੱਕ", "ਸ਼ਨੀ"],
+        ["ਐ", "ਸੋ", "ਮੰ", "ਬੁੱ", "ਵੀ", "ਸ਼ੁੱ", "ਸ਼"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ਈ. ਪੂ.", "ਸੰਨ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pa-Arab` locale.
+pub static PA_ARAB: Locale<'static> = Locale {
+    lang: "pa-Arab",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئ",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئ",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["اتوار", "پیر", "منگل", "بُدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بُدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بُدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ايساپورو", "سں"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pa-Guru` locale.
+pub static PA_GURU: Locale<'static> = Locale {
+    lang: "pa-Guru",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ਜਨਵਰੀ",
+            "ਫ਼ਰਵਰੀ",
+            "ਮਾਰਚ",
+            "ਅਪ੍ਰੈਲ",
+            "ਮਈ",
+            "ਜੂਨ",
+            "ਜੁਲਾਈ",
+            "ਅਗਸਤ",
+            "ਸਤੰਬਰ",
+            "ਅਕਤੂਬਰ",
+            "ਨਵੰਬਰ",
+            "ਦਸੰਬਰ",
+        ],
+        [
+            "ਜਨ",
+            "ਫ਼ਰ",
+            "ਮਾਰਚ",
+            "ਅਪ੍ਰੈ",
+            "ਮਈ",
+            "ਜੂਨ",
+            "ਜੁਲਾ",
+            "ਅਗ",
+            "ਸਤੰ",
+            "ਅਕਤੂ",
+            "ਨਵੰ",
+            "ਦਸੰ",
+        ],
+        ["ਜ", "ਫ਼", "ਮਾ", "ਅ", "ਮ", "ਜੂ", "ਜੁ", "ਅ", "ਸ", "ਅ", "ਨ", "ਦ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ਐਤਵਾਰ",
+            "ਸੋਮਵਾਰ",
+            "ਮੰਗਲਵਾਰ",
+            "ਬੁੱਧਵਾਰ",
+            "ਵੀਰਵਾਰ",
+            "ਸ਼ੁੱਕਰਵਾਰ",
+            "ਸ਼ਨੀਵਾਰ",
+        ],
+        ["ਐਤ", "ਸੋਮ", "ਮੰਗਲ", "ਬੁੱਧ", "ਵੀਰ", "ਸ਼ੁੱਕਰ", "ਸ਼ਨੀ"],
+        ["ਐਤ", "ਸੋਮ", "ਮੰਗ", "ਬੁੱਧ", "ਵੀਰ", "ਸ਼ੁੱਕ", "ਸ਼ਨੀ"],
+        ["ਐ", "ਸੋ", "ਮੰ", "ਬੁੱ", "ਵੀ", "ਸ਼ੁੱ", "ਸ਼"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ਈ. ਪੂ.", "ਸੰਨ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pap` locale.
+pub static PAP: Locale<'static> = Locale {
+    lang: "pap",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Yanüari",
+            "Febrüari",
+            "Mart",
+            "Aprel",
+            "Mei",
+            "Yüni",
+            "Yüli",
+            "Ougùstùs",
+            "Sèptèmber",
+            "Òktober",
+            "Novèmber",
+            "Desèmber",
+        ],
+        [
+            "Yanüari",
+            "Febrüari",
+            "Mart",
+            "Aprel",
+            "Mei",
+            "Yüni",
+            "Yüli",
+            "Ougùstùs",
+            "Sèptèmber",
+            "Òktober",
+            "Novèmber",
+            "Desèmber",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pap-AW` locale.
+pub static PAP_AW: Locale<'static> = Locale {
+    lang: "pap-AW",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Yanüari",
+            "Febrüari",
+            "Mart",
+            "Aprel",
+            "Mei",
+            "Yüni",
+            "Yüli",
+            "Ougùstùs",
+            "Sèptèmber",
+            "Òktober",
+            "Novèmber",
+            "Desèmber",
+        ],
+        [
+            "Yanüari",
+            "Febrüari",
+            "Mart",
+            "Aprel",
+            "Mei",
+            "Yüni",
+            "Yüli",
+            "Ougùstùs",
+            "Sèptèmber",
+            "Òktober",
+            "Novèmber",
+            "Desèmber",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        [
+            "djadumingu",
+            "djaluna",
+            "djamars",
+            "djarason",
+            "djaweps",
+            "djabièrnè",
+            "djasabra",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pcm` locale.
+pub static PCM: Locale<'static> = Locale {
+    lang: "pcm",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "H:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Jénúári",
+            "Fẹ́búári",
+            "Mach",
+            "Éprel",
+            "Mee",
+            "Jun",
+            "Julai",
+            "Ọgọst",
+            "Sẹptẹ́mba",
+            "Ọktóba",
+            "Nọvẹ́mba",
+            "Disẹ́mba",
+        ],
+        [
+            "Jén", "Fẹ́b", "Mach", "Épr", "Mee", "Jun", "Jul", "Ọgọ", "Sẹp", "Ọkt", "Nọv", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sọ́ndè",
+            "Mọ́ndè",
+            "Tiúzdè",
+            "Wẹ́nẹ́zdè",
+            "Tọ́zdè",
+            "Fraídè",
+            "Sátọdè",
+        ],
+        ["Sọ́n", "Mọ́n", "Tiú", "Wẹ́n", "Tọ́z", "Fraí", "Sát"],
+        ["Sọ́n", "Mọ́n", "Tiú", "Wẹ́n", "Tọ́z", "Fraí", "Sát"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["Fọ mọ́nin", "Fọ ívnin"], ["FM", "FI"], ["FM", "FI"]),
+    eras: Locale::new_eras("BK", "KIY"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pi` locale.
+pub static PI: Locale<'static> = Locale {
+    lang: "pi",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pi-Latn` locale.
+pub static PI_LATN: Locale<'static> = Locale {
+    lang: "pi-Latn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pis` locale.
+pub static PIS: Locale<'static> = Locale {
+    lang: "pis",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januare", "Febuare", "Mas", "Eprel", "Mei", "Jun", "Julae", "Ogus", "Septemba",
+            "Oktoba", "Novemba", "Disemba",
+        ],
+        [
+            "Januare", "Febuare", "Mas", "Eprel", "Mei", "Jun", "Julae", "Ogus", "Septemba",
+            "Oktoba", "Novemba", "Disemba",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sande", "Mande", "Tiusde", "Wenesde", "Tosde", "Fraede", "Satade",
+        ],
+        [
+            "Sande", "Mande", "Tiusde", "Wenesde", "Tosde", "Fraede", "Satade",
+        ],
+        [
+            "Sande", "Mande", "Tiusde", "Wenesde", "Tosde", "Fraede", "Satade",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `pl` locale.
+pub static PL: Locale<'static> = Locale {
+    lang: "pl",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "stycznia",
+            "lutego",
+            "marca",
+            "kwietnia",
+            "maja",
+            "czerwca",
+            "lipca",
+            "sierpnia",
+            "września",
+            "października",
+            "listopada",
+            "grudnia",
+        ],
+        [
+            "sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru",
+        ],
+        ["s", "l", "m", "k", "m", "c", "l", "s", "w", "p", "l", "g"],
+    ),
+    days: Locale::new_days(
+        [
+            "niedziela",
+            "poniedziałek",
+            "wtorek",
+            "środa",
+            "czwartek",
+            "piątek",
+            "sobota",
+        ],
+        ["niedz.", "pon.", "wt.", "śr.", "czw.", "pt.", "sob."],
+        ["nie", "pon", "wto", "śro", "czw", "pią", "sob"],
+        ["n", "p", "w", "ś", "c", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("p.n.e.", "n.e."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pms` locale.
+pub static PMS: Locale<'static> = Locale {
+    lang: "pms",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "gené",
+            "fërvé",
+            "mars",
+            "avril",
+            "magg",
+            "giugn",
+            "lugn",
+            "agost",
+            "stèmber",
+            "utuber",
+            "novèmber",
+            "dzèmber",
+        ],
+        [
+            "gené",
+            "fërvé",
+            "mars",
+            "avril",
+            "magg",
+            "giugn",
+            "lugn",
+            "agost",
+            "stèmber",
+            "utuber",
+            "novèmber",
+            "dzèmber",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "dumìnica",
+            "lun-es",
+            "màrtes",
+            "merco",
+            "giòbia",
+            "vënner",
+            "saba",
+        ],
+        [
+            "dumìnica",
+            "lun-es",
+            "màrtes",
+            "merco",
+            "giòbia",
+            "vënner",
+            "saba",
+        ],
+        [
+            "dumìnica",
+            "lun-es",
+            "màrtes",
+            "merco",
+            "giòbia",
+            "vënner",
+            "saba",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `prg` locale.
+pub static PRG: Locale<'static> = Locale {
+    lang: "prg",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "rags",
+            "wassarins",
+            "pūlis",
+            "sakkis",
+            "zallaws",
+            "sīmenis",
+            "līpa",
+            "daggis",
+            "sillins",
+            "spallins",
+            "lapkrūtis",
+            "sallaws",
+        ],
+        [
+            "rags",
+            "wassarins",
+            "pūlis",
+            "sakkis",
+            "zallaws",
+            "sīmenis",
+            "līpa",
+            "daggis",
+            "sillins",
+            "spallins",
+            "lapkrūtis",
+            "sallaws",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "nadīli",
+            "panadīli",
+            "wisasīdis",
+            "pussisawaiti",
+            "ketwirtiks",
+            "pēntniks",
+            "sabattika",
+        ],
+        [
+            "nadīli",
+            "panadīli",
+            "wisasīdis",
+            "pussisawaiti",
+            "ketwirtiks",
+            "pēntniks",
+            "sabattika",
+        ],
+        [
+            "nadīli",
+            "panadīli",
+            "wisasīdis",
+            "pussisawaiti",
+            "ketwirtiks",
+            "pēntniks",
+            "sabattika",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ankstāinan", "pa pussideinan"],
+        ["AM", "PM"],
+        ["AM", "PM"],
+    ),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ps` locale.
+pub static PS: Locale<'static> = Locale {
+    lang: "ps",
+    dates: Locale::new_patterns("EEEE د y د MMMM d", "y MMMM d", "y MMM d", "y/M/d"),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss (z)", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوري",
+            "فبروري",
+            "مارچ",
+            "اپریل",
+            "مۍ",
+            "جون",
+            "جولای",
+            "اګست",
+            "سېپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنوري",
+            "فبروري",
+            "مارچ",
+            "اپریل",
+            "مۍ",
+            "جون",
+            "جولای",
+            "اګست",
+            "سېپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["ج", "ف", "م", "ا", "م", "ج", "ج", "ا", "س", "ا", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["غ.م.", "غ.و."], ["AM", "PM"], ["غ.م.", "غ.و."]),
+    eras: Locale::new_eras("له میلاد وړاندې", "م."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ps-PK` locale.
+pub static PS_PK: Locale<'static> = Locale {
+    lang: "ps-PK",
+    dates: Locale::new_patterns("EEEE د y د MMMM d", "y MMMM d", "y MMM d", "y/M/d"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوري",
+            "فبروري",
+            "مارچ",
+            "اپریل",
+            "مۍ",
+            "جون",
+            "جولای",
+            "اګست",
+            "سېپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنوري",
+            "فبروري",
+            "مارچ",
+            "اپریل",
+            "مۍ",
+            "جون",
+            "جولای",
+            "اګست",
+            "سېپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["ج", "ف", "م", "ا", "م", "ج", "ج", "ا", "س", "ا", "ن", "د"],
+    ),
+    days: Locale::new_days(
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["يونۍ", "دونۍ", "درېنۍ", "څلرنۍ", "پينځنۍ", "جمعه", "اونۍ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["غ.م.", "غ.و."], ["AM", "PM"], ["غ.م.", "غ.و."]),
+    eras: Locale::new_eras("له میلاد وړاندې", "م."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `pt` locale.
+pub static PT: Locale<'static> = Locale {
+    lang: "pt",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d 'de' MMM 'de' y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `pt-AO` locale.
+pub static PT_AO: Locale<'static> = Locale {
+    lang: "pt-AO",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-CH` locale.
+pub static PT_CH: Locale<'static> = Locale {
+    lang: "pt-CH",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-CV` locale.
+pub static PT_CV: Locale<'static> = Locale {
+    lang: "pt-CV",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-GQ` locale.
+pub static PT_GQ: Locale<'static> = Locale {
+    lang: "pt-GQ",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-GW` locale.
+pub static PT_GW: Locale<'static> = Locale {
+    lang: "pt-GW",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-LU` locale.
+pub static PT_LU: Locale<'static> = Locale {
+    lang: "pt-LU",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-MO` locale.
+pub static PT_MO: Locale<'static> = Locale {
+    lang: "pt-MO",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-MZ` locale.
+pub static PT_MZ: Locale<'static> = Locale {
+    lang: "pt-MZ",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-PT` locale.
+pub static PT_PT: Locale<'static> = Locale {
+    lang: "pt-PT",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-ST` locale.
+pub static PT_ST: Locale<'static> = Locale {
+    lang: "pt-ST",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `pt-TL` locale.
+pub static PT_TL: Locale<'static> = Locale {
+    lang: "pt-TL",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "dd/MM/y",
+        "dd/MM/yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janeiro",
+            "fevereiro",
+            "março",
+            "abril",
+            "maio",
+            "junho",
+            "julho",
+            "agosto",
+            "setembro",
+            "outubro",
+            "novembro",
+            "dezembro",
+        ],
+        [
+            "jan.", "fev.", "mar.", "abr.", "mai.", "jun.", "jul.", "ago.", "set.", "out.", "nov.",
+            "dez.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "domingo",
+            "segunda-feira",
+            "terça-feira",
+            "quarta-feira",
+            "quinta-feira",
+            "sexta-feira",
+            "sábado",
+        ],
+        [
+            "domingo", "segunda", "terça", "quarta", "quinta", "sexta", "sábado",
+        ],
+        ["dom.", "seg.", "ter.", "qua.", "qui.", "sex.", "sáb."],
+        ["D", "S", "T", "Q", "Q", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["da manhã", "da tarde"],
+        ["a.m.", "p.m."],
+        ["a.m.", "p.m."],
+    ),
+    eras: Locale::new_eras("a.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `qu` locale.
+pub static QU: Locale<'static> = Locale {
+    lang: "qu",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{0} {1}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Enero",
+            "Febrero",
+            "Marzo",
+            "Abril",
+            "Mayo",
+            "Junio",
+            "Julio",
+            "Agosto",
+            "Setiembre",
+            "Octubre",
+            "Noviembre",
+            "Diciembre",
+        ],
+        [
+            "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Dic",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Domingo",
+            "Lunes",
+            "Martes",
+            "Miércoles",
+            "Jueves",
+            "Viernes",
+            "Sábado",
+        ],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.d.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `qu-BO` locale.
+pub static QU_BO: Locale<'static> = Locale {
+    lang: "qu-BO",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{0} {1}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Enero",
+            "Febrero",
+            "Marzo",
+            "Abril",
+            "Mayo",
+            "Junio",
+            "Julio",
+            "Agosto",
+            "Setiembre",
+            "Octubre",
+            "Noviembre",
+            "Diciembre",
+        ],
+        [
+            "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Dic",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Domingo",
+            "Lunes",
+            "Martes",
+            "Miércoles",
+            "Jueves",
+            "Viernes",
+            "Sábado",
+        ],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.d.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `qu-EC` locale.
+pub static QU_EC: Locale<'static> = Locale {
+    lang: "qu-EC",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{0} {1}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Enero",
+            "Febrero",
+            "Marzo",
+            "Abril",
+            "Mayo",
+            "Junio",
+            "Julio",
+            "Agosto",
+            "Setiembre",
+            "Octubre",
+            "Noviembre",
+            "Diciembre",
+        ],
+        [
+            "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Dic",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Domingo",
+            "Lunes",
+            "Martes",
+            "Miércoles",
+            "Jueves",
+            "Viernes",
+            "Sábado",
+        ],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sab"],
+        ["D", "L", "M", "X", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("a.d.", "d.C."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `quc` locale.
+pub static QUC: Locale<'static> = Locale {
+    lang: "quc",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `raj` locale.
+pub static RAJ: Locale<'static> = Locale {
+    lang: "raj",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जनवरी",
+            "फरवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितम्बर",
+            "अक्टूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रविवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "गुरुवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        [
+            "रविवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "गुरुवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        [
+            "रविवार",
+            "सोमवार",
+            "मंगलवार",
+            "बुधवार",
+            "गुरुवार",
+            "शुक्रवार",
+            "शनिवार",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["सबेरे", "सांम"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `rhg` locale.
+pub static RHG: Locale<'static> = Locale {
+    lang: "rhg",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `rhg-Rohg` locale.
+pub static RHG_ROHG: Locale<'static> = Locale {
+    lang: "rhg-Rohg",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `rhg-Rohg-BD` locale.
+pub static RHG_ROHG_BD: Locale<'static> = Locale {
+    lang: "rhg-Rohg-BD",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `rif` locale.
+pub static RIF: Locale<'static> = Locale {
+    lang: "rif",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `rm` locale.
+pub static RM: Locale<'static> = Locale {
+    lang: "rm",
+    dates: Locale::new_patterns("EEEE, 'ils' d MMMM y", "d MMMM y", "dd-MM-y", "dd-MM-yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "da schaner",
+            "da favrer",
+            "da mars",
+            "d’avrigl",
+            "da matg",
+            "da zercladur",
+            "da fanadur",
+            "d’avust",
+            "da settember",
+            "d’october",
+            "da november",
+            "da december",
+        ],
+        [
+            "schan.", "favr.", "mars", "avr.", "matg", "zercl.", "fan.", "avust", "sett.", "oct.",
+            "nov.", "dec.",
+        ],
+        ["S", "F", "M", "A", "M", "Z", "F", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dumengia",
+            "glindesdi",
+            "mardi",
+            "mesemna",
+            "gievgia",
+            "venderdi",
+            "sonda",
+        ],
+        ["du", "gli", "ma", "me", "gie", "ve", "so"],
+        ["du", "gli", "ma", "me", "gie", "ve", "so"],
+        ["D", "G", "M", "M", "G", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("a.Cr.", "s.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `rn` locale.
+pub static RN: Locale<'static> = Locale {
+    lang: "rn",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Nzero",
+            "Ruhuhuma",
+            "Ntwarante",
+            "Ndamukiza",
+            "Rusama",
+            "Ruheshi",
+            "Mukakaro",
+            "Nyandagaro",
+            "Nyakanga",
+            "Gitugutu",
+            "Munyonyo",
+            "Kigarama",
+        ],
+        [
+            "Mut.", "Gas.", "Wer.", "Mat.", "Gic.", "Kam.", "Nya.", "Kan.", "Nze.", "Ukw.", "Ugu.",
+            "Uku.",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Ku w’indwi",
+            "Ku wa mbere",
+            "Ku wa kabiri",
+            "Ku wa gatatu",
+            "Ku wa kane",
+            "Ku wa gatanu",
+            "Ku wa gatandatu",
+        ],
+        ["cu.", "mbe.", "kab.", "gtu.", "kan.", "gnu.", "gnd."],
+        ["cu.", "mbe.", "kab.", "gtu.", "kan.", "gnu.", "gnd."],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Z.MU.", "Z.MW."],
+        ["Z.MU.", "Z.MW."],
+        ["Z.MU.", "Z.MW."],
+    ),
+    eras: Locale::new_eras("Mb.Y.", "Ny.Y"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ro` locale.
+pub static RO: Locale<'static> = Locale {
+    lang: "ro",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ianuarie",
+            "februarie",
+            "martie",
+            "aprilie",
+            "mai",
+            "iunie",
+            "iulie",
+            "august",
+            "septembrie",
+            "octombrie",
+            "noiembrie",
+            "decembrie",
+        ],
+        [
+            "ian.", "feb.", "mar.", "apr.", "mai", "iun.", "iul.", "aug.", "sept.", "oct.", "nov.",
+            "dec.",
+        ],
+        ["I", "F", "M", "A", "M", "I", "I", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "duminică",
+            "luni",
+            "marți",
+            "miercuri",
+            "joi",
+            "vineri",
+            "sâmbătă",
+        ],
+        ["dum.", "lun.", "mar.", "mie.", "joi", "vin.", "sâm."],
+        ["du.", "lu.", "ma.", "mi.", "joi", "vi.", "sâ."],
+        ["D", "L", "M", "M", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("î.Hr.", "d.Hr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ro-MD` locale.
+pub static RO_MD: Locale<'static> = Locale {
+    lang: "ro-MD",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ianuarie",
+            "februarie",
+            "martie",
+            "aprilie",
+            "mai",
+            "iunie",
+            "iulie",
+            "august",
+            "septembrie",
+            "octombrie",
+            "noiembrie",
+            "decembrie",
+        ],
+        [
+            "ian.", "feb.", "mar.", "apr.", "mai", "iun.", "iul.", "aug.", "sept.", "oct.", "nov.",
+            "dec.",
+        ],
+        ["I", "F", "M", "A", "M", "I", "I", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "duminică",
+            "luni",
+            "marți",
+            "miercuri",
+            "joi",
+            "vineri",
+            "sâmbătă",
+        ],
+        ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm"],
+        ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ"],
+        ["D", "L", "Ma", "Mi", "J", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["a.m.", "p.m."], ["a.m.", "p.m."], ["a.m.", "p.m."]),
+    eras: Locale::new_eras("î.Hr.", "d.Hr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `rof` locale.
+pub static ROF: Locale<'static> = Locale {
+    lang: "rof",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mweri wa kwanza",
+            "Mweri wa kaili",
+            "Mweri wa katatu",
+            "Mweri wa kaana",
+            "Mweri wa tanu",
+            "Mweri wa sita",
+            "Mweri wa saba",
+            "Mweri wa nane",
+            "Mweri wa tisa",
+            "Mweri wa ikumi",
+            "Mweri wa ikumi na moja",
+            "Mweri wa ikumi na mbili",
+        ],
+        [
+            "M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9", "M10", "M11", "M12",
+        ],
+        ["K", "K", "K", "K", "T", "S", "S", "N", "T", "I", "I", "I"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ijumapili",
+            "Ijumatatu",
+            "Ijumanne",
+            "Ijumatano",
+            "Alhamisi",
+            "Ijumaa",
+            "Ijumamosi",
+        ],
+        ["Ijp", "Ijt", "Ijn", "Ijtn", "Alh", "Iju", "Ijm"],
+        ["Ijp", "Ijt", "Ijn", "Ijtn", "Alh", "Iju", "Ijm"],
+        ["2", "3", "4", "5", "6", "7", "1"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["kang’ama", "kingoto"],
+        ["kang’ama", "kingoto"],
+        ["kang’ama", "kingoto"],
+    ),
+    eras: Locale::new_eras("KM", "BM"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ru` locale.
+pub static RU: Locale<'static> = Locale {
+    lang: "ru",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ru-BY` locale.
+pub static RU_BY: Locale<'static> = Locale {
+    lang: "ru-BY",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ru-KG` locale.
+pub static RU_KG: Locale<'static> = Locale {
+    lang: "ru-KG",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ru-KZ` locale.
+pub static RU_KZ: Locale<'static> = Locale {
+    lang: "ru-KZ",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ru-MD` locale.
+pub static RU_MD: Locale<'static> = Locale {
+    lang: "ru-MD",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ru-UA` locale.
+pub static RU_UA: Locale<'static> = Locale {
+    lang: "ru-UA",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'г'.",
+        "d MMMM y 'г'.",
+        "d MMM y 'г'.",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "января",
+            "февраля",
+            "марта",
+            "апреля",
+            "мая",
+            "июня",
+            "июля",
+            "августа",
+            "сентября",
+            "октября",
+            "ноября",
+            "декабря",
+        ],
+        [
+            "янв.",
+            "февр.",
+            "мар.",
+            "апр.",
+            "мая",
+            "июн.",
+            "июл.",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "воскресенье",
+            "понедельник",
+            "вторник",
+            "среда",
+            "четверг",
+            "пятница",
+            "суббота",
+        ],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["В", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("до н. э.", "н. э."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `rw` locale.
+pub static RW: Locale<'static> = Locale {
+    lang: "rw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mutarama",
+            "Gashyantare",
+            "Werurwe",
+            "Mata",
+            "Gicurasi",
+            "Kamena",
+            "Nyakanga",
+            "Kanama",
+            "Nzeri",
+            "Ukwakira",
+            "Ugushyingo",
+            "Ukuboza",
+        ],
+        [
+            "Mut.", "Gas.", "Wer.", "Mat.", "Gic.", "Kam.", "Nya.", "Kan.", "Nze.", "Ukw.", "Ugu.",
+            "Uku.",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Ku cyumweru",
+            "Kuwa mbere",
+            "Kuwa kabiri",
+            "Kuwa gatatu",
+            "Kuwa kane",
+            "Kuwa gatanu",
+            "Kuwa gatandatu",
+        ],
+        ["Cyu.", "Mbe.", "Kab.", "Gtu.", "Kan.", "Gnu.", "Gnd."],
+        ["Cyu.", "Mbe.", "Kab.", "Gtu.", "Kan.", "Gnu.", "Gnd."],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `rwk` locale.
+pub static RWK: Locale<'static> = Locale {
+    lang: "rwk",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprilyi", "Mei", "Junyi", "Julyai", "Agusti",
+            "Septemba", "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapilyi",
+            "Jumatatuu",
+            "Jumanne",
+            "Jumatanu",
+            "Alhamisi",
+            "Ijumaa",
+            "Jumamosi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["J", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sa` locale.
+pub static SA: Locale<'static> = Locale {
+    lang: "sa",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरीमासः",
+            "फरवरीमासः",
+            "मार्चमासः",
+            "अप्रैलमासः",
+            "मईमासः",
+            "जूनमासः",
+            "जुलाईमासः",
+            "अगस्तमासः",
+            "सितंबरमासः",
+            "अक्तूबरमासः",
+            "नवंबरमासः",
+            "दिसंबरमासः",
+        ],
+        [
+            "जनवरी:",
+            "फरवरी:",
+            "मार्च:",
+            "अप्रैल:",
+            "मई",
+            "जून:",
+            "जुलाई:",
+            "अगस्त:",
+            "सितंबर:",
+            "अक्तूबर:",
+            "नवंबर:",
+            "दिसंबर:",
+        ],
+        [
+            "ज", "फ", "मा", "अ", "म", "जू", "जु", "अ", "सि", "अ", "न", "दि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "रविवासरः",
+            "सोमवासरः",
+            "मंगलवासरः",
+            "बुधवासरः",
+            "गुरुवासर:",
+            "शुक्रवासरः",
+            "शनिवासरः",
+        ],
+        ["रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि"],
+        ["र", "सो", "मं", "बु", "गु", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(["पूर्वाह्न", "अपराह्न"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sah` locale.
+pub static SAH: Locale<'static> = Locale {
+    lang: "sah",
+    dates: Locale::new_patterns(
+        "y 'сыл' MMMM d 'күнэ', EEEE",
+        "y, MMMM d",
+        "y, MMM d",
+        "yy/M/d",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Тохсунньу",
+            "Олунньу",
+            "Кулун тутар",
+            "Муус устар",
+            "Ыам ыйын",
+            "Бэс ыйын",
+            "От ыйын",
+            "Атырдьых ыйын",
+            "Балаҕан ыйын",
+            "Алтынньы",
+            "Сэтинньи",
+            "ахсынньы",
+        ],
+        [
+            "Тохс", "Олун", "Клн", "Мсу", "Ыам", "Бэс", "Отй", "Атр", "Блҕ", "Алт", "Сэт", "Ахс",
+        ],
+        ["Т", "О", "К", "М", "Ы", "Б", "О", "А", "Б", "А", "С", "А"],
+    ),
+    days: Locale::new_days(
+        [
+            "баскыһыанньа",
+            "бэнидиэнньик",
+            "оптуорунньук",
+            "сэрэдэ",
+            "чэппиэр",
+            "Бээтиҥсэ",
+            "субуота",
+        ],
+        ["бс", "бн", "оп", "сэ", "чп", "бэ", "сб"],
+        ["бс", "бн", "оп", "сэ", "чп", "бэ", "сб"],
+        ["Б", "Б", "О", "С", "Ч", "Б", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["ЭИ", "ЭК"], ["ЭИ", "ЭК"], ["ЭИ", "ЭК"]),
+    eras: Locale::new_eras("б. э. и.", "б. э"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `saq` locale.
+pub static SAQ: Locale<'static> = Locale {
+    lang: "saq",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Lapa le obo",
+            "Lapa le waare",
+            "Lapa le okuni",
+            "Lapa le ong’wan",
+            "Lapa le imet",
+            "Lapa le ile",
+            "Lapa le sapa",
+            "Lapa le isiet",
+            "Lapa le saal",
+            "Lapa le tomon",
+            "Lapa le tomon obo",
+            "Lapa le tomon waare",
+        ],
+        [
+            "Obo", "Waa", "Oku", "Ong", "Ime", "Ile", "Sap", "Isi", "Saa", "Tom", "Tob", "Tow",
+        ],
+        ["O", "W", "O", "O", "I", "I", "S", "I", "S", "T", "T", "T"],
+    ),
+    days: Locale::new_days(
+        [
+            "Mderot ee are",
+            "Mderot ee kuni",
+            "Mderot ee ong’wan",
+            "Mderot ee inet",
+            "Mderot ee ile",
+            "Mderot ee sapa",
+            "Mderot ee kwe",
+        ],
+        ["Are", "Kun", "Ong", "Ine", "Ile", "Sap", "Kwe"],
+        ["Are", "Kun", "Ong", "Ine", "Ile", "Sap", "Kwe"],
+        ["A", "K", "O", "I", "I", "S", "K"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Tesiran", "Teipa"],
+        ["Tesiran", "Teipa"],
+        ["Tesiran", "Teipa"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sat` locale.
+pub static SAT: Locale<'static> = Locale {
+    lang: "sat",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ᱡᱟᱱᱣᱟᱨᱤ",
+            "ᱯᱷᱟᱨᱣᱟᱨᱤ",
+            "ᱢᱟᱨᱪ",
+            "ᱟᱯᱨᱮᱞ",
+            "ᱢᱮ",
+            "ᱡᱩᱱ",
+            "ᱡᱩᱞᱟᱭ",
+            "ᱟᱜᱟᱥᱛ",
+            "ᱥᱮᱯᱴᱮᱢᱵᱟᱨ",
+            "ᱚᱠᱴᱚᱵᱟᱨ",
+            "ᱱᱟᱣᱟᱢᱵᱟᱨ",
+            "ᱫᱤᱥᱟᱢᱵᱟᱨ",
+        ],
+        [
+            "ᱡᱟᱱ",
+            "ᱯᱷᱟ",
+            "ᱢᱟᱨ",
+            "ᱟᱯᱨ",
+            "ᱢᱮ",
+            "ᱡᱩᱱ",
+            "ᱡᱩᱞ",
+            "ᱟᱜᱟ",
+            "ᱥᱮᱯ",
+            "ᱚᱠᱴ",
+            "ᱱᱟᱣ",
+            "ᱫᱤᱥ",
+        ],
+        ["ᱡ", "ᱯ", "ᱢ", "ᱟ", "ᱢ", "ᱡ", "ᱡ", "ᱟ", "ᱥ", "ᱚ", "ᱱ", "ᱫ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ᱥᱤᱸᱜᱮ",
+            "ᱚᱛᱮ",
+            "ᱵᱟᱞᱮ",
+            "ᱥᱟᱹᱜᱩᱱ",
+            "ᱥᱟᱹᱨᱫᱤ",
+            "ᱡᱟᱹᱨᱩᱢ",
+            "ᱧᱩᱦᱩᱢ",
+        ],
+        ["ᱥᱤᱸ", "ᱚᱛ", "ᱵᱟ", "ᱥᱟᱹ", "ᱥᱟᱹᱨ", "ᱡᱟᱹ", "ᱧᱩ"],
+        ["ᱥᱤᱸ", "ᱚᱛ", "ᱵᱟ", "ᱥᱟᱹ", "ᱥᱟᱹᱨ", "ᱡᱟᱹ", "ᱧᱩ"],
+        ["ᱥ", "ᱚ", "ᱵ", "ᱥ", "ᱥ", "ᱡ", "ᱧ"],
+    ),
+    day_periods: Locale::new_day_periods(["ᱥᱮᱛᱟᱜ", "ᱧᱤᱫᱟᱹ"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ᱵᱤ ᱥᱤ", "ᱤᱥᱣᱤ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sat-Deva` locale.
+pub static SAT_DEVA: Locale<'static> = Locale {
+    lang: "sat-Deva",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sat-Olck` locale.
+pub static SAT_OLCK: Locale<'static> = Locale {
+    lang: "sat-Olck",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ᱡᱟᱱᱣᱟᱨᱤ",
+            "ᱯᱷᱟᱨᱣᱟᱨᱤ",
+            "ᱢᱟᱨᱪ",
+            "ᱟᱯᱨᱮᱞ",
+            "ᱢᱮ",
+            "ᱡᱩᱱ",
+            "ᱡᱩᱞᱟᱭ",
+            "ᱟᱜᱟᱥᱛ",
+            "ᱥᱮᱯᱴᱮᱢᱵᱟᱨ",
+            "ᱚᱠᱴᱚᱵᱟᱨ",
+            "ᱱᱟᱣᱟᱢᱵᱟᱨ",
+            "ᱫᱤᱥᱟᱢᱵᱟᱨ",
+        ],
+        [
+            "ᱡᱟᱱ",
+            "ᱯᱷᱟ",
+            "ᱢᱟᱨ",
+            "ᱟᱯᱨ",
+            "ᱢᱮ",
+            "ᱡᱩᱱ",
+            "ᱡᱩᱞ",
+            "ᱟᱜᱟ",
+            "ᱥᱮᱯ",
+            "ᱚᱠᱴ",
+            "ᱱᱟᱣ",
+            "ᱫᱤᱥ",
+        ],
+        ["ᱡ", "ᱯ", "ᱢ", "ᱟ", "ᱢ", "ᱡ", "ᱡ", "ᱟ", "ᱥ", "ᱚ", "ᱱ", "ᱫ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ᱥᱤᱸᱜᱮ",
+            "ᱚᱛᱮ",
+            "ᱵᱟᱞᱮ",
+            "ᱥᱟᱹᱜᱩᱱ",
+            "ᱥᱟᱹᱨᱫᱤ",
+            "ᱡᱟᱹᱨᱩᱢ",
+            "ᱧᱩᱦᱩᱢ",
+        ],
+        ["ᱥᱤᱸ", "ᱚᱛ", "ᱵᱟ", "ᱥᱟᱹ", "ᱥᱟᱹᱨ", "ᱡᱟᱹ", "ᱧᱩ"],
+        ["ᱥᱤᱸ", "ᱚᱛ", "ᱵᱟ", "ᱥᱟᱹ", "ᱥᱟᱹᱨ", "ᱡᱟᱹ", "ᱧᱩ"],
+        ["ᱥ", "ᱚ", "ᱵ", "ᱥ", "ᱥ", "ᱡ", "ᱧ"],
+    ),
+    day_periods: Locale::new_day_periods(["ᱥᱮᱛᱟᱜ", "ᱧᱤᱫᱟᱹ"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ᱵᱤ ᱥᱤ", "ᱤᱥᱣᱤ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sbp` locale.
+pub static SBP: Locale<'static> = Locale {
+    lang: "sbp",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Mupalangulwa",
+            "Mwitope",
+            "Mushende",
+            "Munyi",
+            "Mushende Magali",
+            "Mujimbi",
+            "Mushipepo",
+            "Mupuguto",
+            "Munyense",
+            "Mokhu",
+            "Musongandembwe",
+            "Muhaano",
+        ],
+        [
+            "Mup", "Mwi", "Msh", "Mun", "Mag", "Muj", "Msp", "Mpg", "Mye", "Mok", "Mus", "Muh",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Mulungu",
+            "Jumatatu",
+            "Jumanne",
+            "Jumatano",
+            "Alahamisi",
+            "Ijumaa",
+            "Jumamosi",
+        ],
+        ["Mul", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Mul", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["M", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Lwamilawu", "Pashamihe"],
+        ["Lwamilawu", "Pashamihe"],
+        ["Lwamilawu", "Pashamihe"],
+    ),
+    eras: Locale::new_eras("AK", "PK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sc` locale.
+pub static SC: Locale<'static> = Locale {
+    lang: "sc",
+    dates: Locale::new_patterns(
+        "EEEE d 'de' MMMM 'de' 'su' y",
+        "d 'de' MMMM 'de' 'su' y",
+        "d 'de' MMM y",
+        "dd/MM/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ghennàrgiu",
+            "freàrgiu",
+            "martzu",
+            "abrile",
+            "maju",
+            "làmpadas",
+            "trìulas",
+            "austu",
+            "cabudanni",
+            "santugaine",
+            "santandria",
+            "nadale",
+        ],
+        [
+            "ghe", "fre", "mar", "abr", "maj", "làm", "trì", "aus", "cab", "stG", "stA", "nad",
+        ],
+        ["G", "F", "M", "A", "M", "L", "T", "A", "C", "S", "S", "N"],
+    ),
+    days: Locale::new_days(
+        [
+            "domìniga",
+            "lunis",
+            "martis",
+            "mèrcuris",
+            "giòbia",
+            "chenàbura",
+            "sàbadu",
+        ],
+        ["dom", "lun", "mar", "mèr", "giò", "che", "sàb"],
+        ["dom", "lun", "mar", "mèr", "giò", "che", "sàb"],
+        ["D", "L", "M", "M", "G", "C", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["m.", "b."]),
+    eras: Locale::new_eras("a.C.", "p.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `scn` locale.
+pub static SCN: Locale<'static> = Locale {
+    lang: "scn",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "jinnaru",
+            "frivaru",
+            "marzu",
+            "aprili",
+            "maju",
+            "giugnu",
+            "giugnettu",
+            "agustu",
+            "sittèmmiru",
+            "uttùviru",
+            "nuvèmmiru",
+            "dicèmmiru",
+        ],
+        [
+            "jin", "fri", "mar", "apr", "maj", "giu", "gnt", "agu", "sit", "utt", "nuv", "dic",
+        ],
+        ["J", "F", "M", "A", "M", "G", "G", "A", "S", "U", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "dumìnica",
+            "lunnidìa",
+            "martidìa",
+            "mercuridìa",
+            "jovidìa",
+            "venniridìa",
+            "sàbbatu",
+        ],
+        ["dum", "lun", "mar", "mer", "jov", "ven", "sab"],
+        ["du", "lu", "ma", "me", "jo", "ve", "sa"],
+        ["d", "l", "m", "m", "j", "v", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("p.C.", "d.C."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sd` locale.
+pub static SD: Locale<'static> = Locale {
+    lang: "sd",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوري",
+            "فيبروري",
+            "مارچ",
+            "اپريل",
+            "مئي",
+            "جون",
+            "جولاءِ",
+            "آگسٽ",
+            "سيپٽمبر",
+            "آڪٽوبر",
+            "نومبر",
+            "ڊسمبر",
+        ],
+        [
+            "جنوري",
+            "فيبروري",
+            "مارچ",
+            "اپريل",
+            "مئي",
+            "جون",
+            "جولاءِ",
+            "آگسٽ",
+            "سيپٽمبر",
+            "آڪٽوبر",
+            "نومبر",
+            "ڊسمبر",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سو", "اڱ", "اربع", "خم", "جم", "ڇن"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["صبح، منجهند", "منجهند، شام"],
+        ["صبح", "شام"],
+        ["صبح", "شام"],
+    ),
+    eras: Locale::new_eras("ق م", "ع"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sd-Arab` locale.
+pub static SD_ARAB: Locale<'static> = Locale {
+    lang: "sd-Arab",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوري",
+            "فيبروري",
+            "مارچ",
+            "اپريل",
+            "مئي",
+            "جون",
+            "جولاءِ",
+            "آگسٽ",
+            "سيپٽمبر",
+            "آڪٽوبر",
+            "نومبر",
+            "ڊسمبر",
+        ],
+        [
+            "جنوري",
+            "فيبروري",
+            "مارچ",
+            "اپريل",
+            "مئي",
+            "جون",
+            "جولاءِ",
+            "آگسٽ",
+            "سيپٽمبر",
+            "آڪٽوبر",
+            "نومبر",
+            "ڊسمبر",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سومر", "اڱارو", "اربع", "خميس", "جمعو", "ڇنڇر"],
+        ["آچر", "سو", "اڱ", "اربع", "خم", "جم", "ڇن"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["صبح، منجهند", "منجهند، شام"],
+        ["صبح", "شام"],
+        ["صبح", "شام"],
+    ),
+    eras: Locale::new_eras("ق م", "ع"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sd-Deva` locale.
+pub static SD_DEVA: Locale<'static> = Locale {
+    lang: "sd-Deva",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फेबरवरी",
+            "मार्चु",
+            "अप्रेल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "आगस्ट",
+            "सप्टेंबर",
+            "आक्टोबर",
+            "नवंबर",
+            "डिसंबर",
+        ],
+        [
+            "जन",
+            "फर",
+            "मार्च",
+            "अप्रै",
+            "मई",
+            "जून",
+            "जु",
+            "अग",
+            "सप्टे",
+            "ऑक्टो",
+            "नवं",
+            "डिसं",
+        ],
+        [
+            "ज", "फ़", "मा", "अ", "मा", "जू", "जु", "अग", "स", "ऑ", "न", "डि",
+        ],
+    ),
+    days: Locale::new_days(
+        ["आर्तवार", "सूमर", "मंगलु", "ॿुधर", "विस्पत", "जुमो", "छंछर"],
+        ["आर्त", "सू", "मंग", "ॿुध", "विस", "जुम", "छंछ"],
+        ["आर्त", "सू", "मंग", "ॿुध", "विस", "जुम", "छंछ"],
+        ["आ", "सू", "मं", "ॿु", "वि", "जु", "छं"],
+    ),
+    day_periods: Locale::new_day_periods(["सुबुह जा", "शाम जा"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("बीसी", "एडी"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sdh` locale.
+pub static SDH: Locale<'static> = Locale {
+    lang: "sdh",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sdh-IQ` locale.
+pub static SDH_IQ: Locale<'static> = Locale {
+    lang: "sdh-IQ",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `se` locale.
+pub static SE: Locale<'static> = Locale {
+    lang: "se",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ođđajagemánnu",
+            "guovvamánnu",
+            "njukčamánnu",
+            "cuoŋománnu",
+            "miessemánnu",
+            "geassemánnu",
+            "suoidnemánnu",
+            "borgemánnu",
+            "čakčamánnu",
+            "golggotmánnu",
+            "skábmamánnu",
+            "juovlamánnu",
+        ],
+        [
+            "ođđj", "guov", "njuk", "cuo", "mies", "geas", "suoi", "borg", "čakč", "golg", "skáb",
+            "juov",
+        ],
+        ["O", "G", "N", "C", "M", "G", "S", "B", "Č", "G", "S", "J"],
+    ),
+    days: Locale::new_days(
+        [
+            "sotnabeaivi",
+            "vuossárga",
+            "maŋŋebárga",
+            "gaskavahkku",
+            "duorasdat",
+            "bearjadat",
+            "lávvardat",
+        ],
+        ["sotn", "vuos", "maŋ", "gask", "duor", "bear", "láv"],
+        ["sotn", "vuos", "maŋ", "gask", "duor", "bear", "láv"],
+        ["S", "V", "M", "G", "D", "B", "L"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["iđitbeaivet", "eahketbeaivet"],
+        ["i.b.", "e.b."],
+        ["i.b.", "e.b."],
+    ),
+    eras: Locale::new_eras("o.Kr.", "m.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `se-FI` locale.
+pub static SE_FI: Locale<'static> = Locale {
+    lang: "se-FI",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ođđajagemánnu",
+            "guovvamánnu",
+            "njukčamánnu",
+            "cuoŋománnu",
+            "miessemánnu",
+            "geassemánnu",
+            "suoidnemánnu",
+            "borgemánnu",
+            "čakčamánnu",
+            "golggotmánnu",
+            "skábmamánnu",
+            "juovlamánnu",
+        ],
+        [
+            "ođđj", "guov", "njuk", "cuoŋ", "mies", "geas", "suoi", "borg", "čakč", "golg", "skáb",
+            "juov",
+        ],
+        ["O", "G", "N", "C", "M", "G", "S", "B", "Č", "G", "S", "J"],
+    ),
+    days: Locale::new_days(
+        [
+            "sotnabeaivi",
+            "mánnodat",
+            "disdat",
+            "gaskavahkku",
+            "duorastat",
+            "bearjadat",
+            "lávvordat",
+        ],
+        ["so", "má", "di", "ga", "du", "be", "lá"],
+        ["so", "má", "di", "ga", "du", "be", "lá"],
+        ["S", "M", "D", "G", "D", "B", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["ib", "eb"], ["ib", "eb"], ["i", "e"]),
+    eras: Locale::new_eras("oKr.", "mKr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `se-SE` locale.
+pub static SE_SE: Locale<'static> = Locale {
+    lang: "se-SE",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ođđajagemánnu",
+            "guovvamánnu",
+            "njukčamánnu",
+            "cuoŋománnu",
+            "miessemánnu",
+            "geassemánnu",
+            "suoidnemánnu",
+            "borgemánnu",
+            "čakčamánnu",
+            "golggotmánnu",
+            "skábmamánnu",
+            "juovlamánnu",
+        ],
+        [
+            "ođđj", "guov", "njuk", "cuo", "mies", "geas", "suoi", "borg", "čakč", "golg", "skáb",
+            "juov",
+        ],
+        ["O", "G", "N", "C", "M", "G", "S", "B", "Č", "G", "S", "J"],
+    ),
+    days: Locale::new_days(
+        [
+            "sotnabeaivi",
+            "vuossárga",
+            "maŋŋebárga",
+            "gaskavahkku",
+            "duorasdat",
+            "bearjadat",
+            "lávvardat",
+        ],
+        ["sotn", "vuos", "maŋ", "gask", "duor", "bear", "láv"],
+        ["sotn", "vuos", "maŋ", "gask", "duor", "bear", "láv"],
+        ["S", "V", "M", "G", "D", "B", "L"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["iđitbeaivet", "eahketbeaivet"],
+        ["i.b.", "e.b."],
+        ["i.b.", "e.b."],
+    ),
+    eras: Locale::new_eras("o.Kr.", "m.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `seh` locale.
+pub static SEH: Locale<'static> = Locale {
+    lang: "seh",
+    dates: Locale::new_patterns(
+        "EEEE, d 'de' MMMM 'de' y",
+        "d 'de' MMMM 'de' y",
+        "d 'de' MMM 'de' y",
+        "d/M/y",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janeiro", "Fevreiro", "Marco", "Abril", "Maio", "Junho", "Julho", "Augusto",
+            "Setembro", "Otubro", "Novembro", "Decembro",
+        ],
+        [
+            "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Aug", "Set", "Otu", "Nov", "Dec",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Dimingu", "Chiposi", "Chipiri", "Chitatu", "Chinai", "Chishanu", "Sabudu",
+        ],
+        ["Dim", "Pos", "Pir", "Tat", "Nai", "Sha", "Sab"],
+        ["Dim", "Pos", "Pir", "Tat", "Nai", "Sha", "Sab"],
+        ["D", "P", "C", "T", "N", "S", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("AC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ses` locale.
+pub static SES: Locale<'static> = Locale {
+    lang: "ses",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Žanwiye",
+            "Feewiriye",
+            "Marsi",
+            "Awiril",
+            "Me",
+            "Žuweŋ",
+            "Žuyye",
+            "Ut",
+            "Sektanbur",
+            "Oktoobur",
+            "Noowanbur",
+            "Deesanbur",
+        ],
+        [
+            "Žan", "Fee", "Mar", "Awi", "Me", "Žuw", "Žuy", "Ut", "Sek", "Okt", "Noo", "Dee",
+        ],
+        ["Ž", "F", "M", "A", "M", "Ž", "Ž", "U", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Alhadi",
+            "Atinni",
+            "Atalaata",
+            "Alarba",
+            "Alhamiisa",
+            "Alzuma",
+            "Asibti",
+        ],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["H", "T", "T", "L", "L", "L", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Adduha", "Aluula"],
+        ["Adduha", "Aluula"],
+        ["Adduha", "Aluula"],
+    ),
+    eras: Locale::new_eras("IJ", "IZ"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `sg` locale.
+pub static SG: Locale<'static> = Locale {
+    lang: "sg",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Nyenye",
+            "Fulundïgi",
+            "Mbängü",
+            "Ngubùe",
+            "Bêläwü",
+            "Föndo",
+            "Lengua",
+            "Kükürü",
+            "Mvuka",
+            "Ngberere",
+            "Nabändüru",
+            "Kakauka",
+        ],
+        [
+            "Nye", "Ful", "Mbä", "Ngu", "Bêl", "Fön", "Len", "Kük", "Mvu", "Ngb", "Nab", "Kak",
+        ],
+        ["N", "F", "M", "N", "B", "F", "L", "K", "M", "N", "N", "K"],
+    ),
+    days: Locale::new_days(
+        [
+            "Bikua-ôko",
+            "Bïkua-ûse",
+            "Bïkua-ptâ",
+            "Bïkua-usïö",
+            "Bïkua-okü",
+            "Lâpôsö",
+            "Lâyenga",
+        ],
+        ["Bk1", "Bk2", "Bk3", "Bk4", "Bk5", "Lâp", "Lây"],
+        ["Bk1", "Bk2", "Bk3", "Bk4", "Bk5", "Lâp", "Lây"],
+        ["K", "S", "T", "S", "K", "P", "Y"],
+    ),
+    day_periods: Locale::new_day_periods(["ND", "LK"], ["ND", "LK"], ["ND", "LK"]),
+    eras: Locale::new_eras("KnK", "NpK"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sgs` locale.
+pub static SGS: Locale<'static> = Locale {
+    lang: "sgs",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `shi` locale.
+pub static SHI: Locale<'static> = Locale {
+    lang: "shi",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ⵉⵏⵏⴰⵢⵔ",
+            "ⴱⵕⴰⵢⵕ",
+            "ⵎⴰⵕⵚ",
+            "ⵉⴱⵔⵉⵔ",
+            "ⵎⴰⵢⵢⵓ",
+            "ⵢⵓⵏⵢⵓ",
+            "ⵢⵓⵍⵢⵓⵣ",
+            "ⵖⵓⵛⵜ",
+            "ⵛⵓⵜⴰⵏⴱⵉⵔ",
+            "ⴽⵜⵓⴱⵔ",
+            "ⵏⵓⵡⴰⵏⴱⵉⵔ",
+            "ⴷⵓⵊⴰⵏⴱⵉⵔ",
+        ],
+        [
+            "ⵉⵏⵏ",
+            "ⴱⵕⴰ",
+            "ⵎⴰⵕ",
+            "ⵉⴱⵔ",
+            "ⵎⴰⵢ",
+            "ⵢⵓⵏ",
+            "ⵢⵓⵍ",
+            "ⵖⵓⵛ",
+            "ⵛⵓⵜ",
+            "ⴽⵜⵓ",
+            "ⵏⵓⵡ",
+            "ⴷⵓⵊ",
+        ],
+        ["ⵉ", "ⴱ", "ⵎ", "ⵉ", "ⵎ", "ⵢ", "ⵢ", "ⵖ", "ⵛ", "ⴽ", "ⵏ", "ⴷ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ⴰⵙⴰⵎⴰⵙ",
+            "ⴰⵢⵏⴰⵙ",
+            "ⴰⵙⵉⵏⴰⵙ",
+            "ⴰⴽⵕⴰⵙ",
+            "ⴰⴽⵡⴰⵙ",
+            "ⵙⵉⵎⵡⴰⵙ",
+            "ⴰⵙⵉⴹⵢⴰⵙ",
+        ],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+    ),
+    eras: Locale::new_eras("ⴷⴰⵄ", "ⴷⴼⵄ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `shi-Latn` locale.
+pub static SHI_LATN: Locale<'static> = Locale {
+    lang: "shi-Latn",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "innayr",
+            "bṛayṛ",
+            "maṛṣ",
+            "ibrir",
+            "mayyu",
+            "yunyu",
+            "yulyuz",
+            "ɣuct",
+            "cutanbir",
+            "ktubr",
+            "nuwanbir",
+            "dujanbir",
+        ],
+        [
+            "inn", "bṛa", "maṛ", "ibr", "may", "yun", "yul", "ɣuc", "cut", "ktu", "nuw", "duj",
+        ],
+        ["i", "b", "m", "i", "m", "y", "y", "ɣ", "c", "k", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "asamas",
+            "aynas",
+            "asinas",
+            "akṛas",
+            "akwas",
+            "asimwas",
+            "asiḍyas",
+        ],
+        ["asa", "ayn", "asi", "akṛ", "akw", "asim", "asiḍ"],
+        ["asa", "ayn", "asi", "akṛ", "akw", "asim", "asiḍ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["tifawt", "tadggʷat"],
+        ["tifawt", "tadggʷat"],
+        ["tifawt", "tadggʷat"],
+    ),
+    eras: Locale::new_eras("daɛ", "dfɛ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `shi-Tfng` locale.
+pub static SHI_TFNG: Locale<'static> = Locale {
+    lang: "shi-Tfng",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ⵉⵏⵏⴰⵢⵔ",
+            "ⴱⵕⴰⵢⵕ",
+            "ⵎⴰⵕⵚ",
+            "ⵉⴱⵔⵉⵔ",
+            "ⵎⴰⵢⵢⵓ",
+            "ⵢⵓⵏⵢⵓ",
+            "ⵢⵓⵍⵢⵓⵣ",
+            "ⵖⵓⵛⵜ",
+            "ⵛⵓⵜⴰⵏⴱⵉⵔ",
+            "ⴽⵜⵓⴱⵔ",
+            "ⵏⵓⵡⴰⵏⴱⵉⵔ",
+            "ⴷⵓⵊⴰⵏⴱⵉⵔ",
+        ],
+        [
+            "ⵉⵏⵏ",
+            "ⴱⵕⴰ",
+            "ⵎⴰⵕ",
+            "ⵉⴱⵔ",
+            "ⵎⴰⵢ",
+            "ⵢⵓⵏ",
+            "ⵢⵓⵍ",
+            "ⵖⵓⵛ",
+            "ⵛⵓⵜ",
+            "ⴽⵜⵓ",
+            "ⵏⵓⵡ",
+            "ⴷⵓⵊ",
+        ],
+        ["ⵉ", "ⴱ", "ⵎ", "ⵉ", "ⵎ", "ⵢ", "ⵢ", "ⵖ", "ⵛ", "ⴽ", "ⵏ", "ⴷ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ⴰⵙⴰⵎⴰⵙ",
+            "ⴰⵢⵏⴰⵙ",
+            "ⴰⵙⵉⵏⴰⵙ",
+            "ⴰⴽⵕⴰⵙ",
+            "ⴰⴽⵡⴰⵙ",
+            "ⵙⵉⵎⵡⴰⵙ",
+            "ⴰⵙⵉⴹⵢⴰⵙ",
+        ],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+    ),
+    eras: Locale::new_eras("ⴷⴰⵄ", "ⴷⴼⵄ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `shn` locale.
+pub static SHN: Locale<'static> = Locale {
+    lang: "shn",
+    dates: Locale::new_patterns("y MMMM d - EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ၸၼ်ႇဝႃႇရီႇ",
+            "ၾႅပ်ႇဝႃႇရီႇ",
+            "မၢတ်ႉၶ်ျ",
+            "ဢေႇပရႄႇ",
+            "မေႇ",
+            "ၸုၼ်ႇ",
+            "ၸူႇလၢႆႇ",
+            "ဢေႃးၵၢတ်ႉ",
+            "သႅပ်ႇထႅမ်ႇပႃႇ",
+            "ဢွၵ်ႇထူဝ်ႇပႃႇ",
+            "ၼူဝ်ႇဝႅမ်ႇပႃႇ",
+            "တီႇသႅမ်ႇပႃႇ",
+        ],
+        [
+            "ၸၼ်ႇ",
+            "ၾႅပ်ႇ",
+            "မၢတ်ႉၶ်ျ",
+            "ဢေႇ",
+            "မေႇ",
+            "ၸုၼ်ႇ",
+            "ၸူႇ",
+            "ဢေႃး",
+            "သႅပ်ႇ",
+            "ဢွၵ်ႇ",
+            "ၼူဝ်ႇ",
+            "တီႇ",
+        ],
+        [
+            "ၸ.", "ၾ.", "မ.", "ဢ.", "မ.", "ၸ.", "ၸ.", "ဢ.", "သ.", "ဢ.", "ၼ.", "တ.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ဝၼ်းဢႃးတိတ်ႉ",
+            "ဝၼ်းၸၼ်",
+            "ဝၼ်းဢင်းၵၢၼ်း",
+            "ဝၼ်းပုတ်ႉ",
+            "ဝၼ်းၽတ်း",
+            "ဝၼ်းသုၵ်း",
+            "ဝၼ်းသဝ်",
+        ],
+        ["တိတ်ႉ", "ၸၼ်", "ၵၢၼ်း", "ပုတ်ႉ", "ၽတ်း", "သုၵ်း", "သဝ်"],
+        ["တိတ်ႉ", "ၸၼ်", "ၵၢၼ်း", "ပုတ်ႉ", "ၽတ်း", "သုၵ်း", "သဝ်"],
+        ["တိ.", "ၸ.", "ၵ.", "ပု.", "ၽ.", "သု.", "သ."],
+    ),
+    day_periods: Locale::new_day_periods(["တွၼ်ႈၸဝ်ႉ", "တွၼ်ႈလႃႈ"], ["တၸ.", "တလ."], ["ၸ.", "လ."]),
+    eras: Locale::new_eras("ပီႇၸီႇ", "ဢေႇတီႇ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `shn-TH` locale.
+pub static SHN_TH: Locale<'static> = Locale {
+    lang: "shn-TH",
+    dates: Locale::new_patterns("y MMMM d - EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ၸၼ်ႇဝႃႇရီႇ",
+            "ၾႅပ်ႇဝႃႇရီႇ",
+            "မၢတ်ႉၶ်ျ",
+            "ဢေႇပရႄႇ",
+            "မေႇ",
+            "ၸုၼ်ႇ",
+            "ၸူႇလၢႆႇ",
+            "ဢေႃးၵၢတ်ႉ",
+            "သႅပ်ႇထႅမ်ႇပႃႇ",
+            "ဢွၵ်ႇထူဝ်ႇပႃႇ",
+            "ၼူဝ်ႇဝႅမ်ႇပႃႇ",
+            "တီႇသႅမ်ႇပႃႇ",
+        ],
+        [
+            "ၸၼ်ႇ",
+            "ၾႅပ်ႇ",
+            "မၢတ်ႉၶ်ျ",
+            "ဢေႇ",
+            "မေႇ",
+            "ၸုၼ်ႇ",
+            "ၸူႇ",
+            "ဢေႃး",
+            "သႅပ်ႇ",
+            "ဢွၵ်ႇ",
+            "ၼူဝ်ႇ",
+            "တီႇ",
+        ],
+        [
+            "ၸ.", "ၾ.", "မ.", "ဢ.", "မ.", "ၸ.", "ၸ.", "ဢ.", "သ.", "ဢ.", "ၼ.", "တ.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ဝၼ်းဢႃးတိတ်ႉ",
+            "ဝၼ်းၸၼ်",
+            "ဝၼ်းဢင်းၵၢၼ်း",
+            "ဝၼ်းပုတ်ႉ",
+            "ဝၼ်းၽတ်း",
+            "ဝၼ်းသုၵ်း",
+            "ဝၼ်းသဝ်",
+        ],
+        ["တိတ်ႉ", "ၸၼ်", "ၵၢၼ်း", "ပုတ်ႉ", "ၽတ်း", "သုၵ်း", "သဝ်"],
+        ["တိတ်ႉ", "ၸၼ်", "ၵၢၼ်း", "ပုတ်ႉ", "ၽတ်း", "သုၵ်း", "သဝ်"],
+        ["တိ.", "ၸ.", "ၵ.", "ပု.", "ၽ.", "သု.", "သ."],
+    ),
+    day_periods: Locale::new_day_periods(["တွၼ်ႈၸဝ်ႉ", "တွၼ်ႈလႃႈ"], ["တၸ.", "တလ."], ["ၸ.", "လ."]),
+    eras: Locale::new_eras("ပီႇၸီႇ", "ဢေႇတီႇ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `si` locale.
+pub static SI: Locale<'static> = Locale {
+    lang: "si",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "HH.mm.ss z", "HH.mm.ss", "HH.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "දුරුතු",
+            "නවම්",
+            "මැදින්",
+            "බක්",
+            "වෙසක්",
+            "පොසොන්",
+            "ඇසළ",
+            "නිකිණි",
+            "බිනර",
+            "වප්",
+            "ඉල්",
+            "උඳුවප්",
+        ],
+        [
+            "දුරුතු",
+            "නවම්",
+            "මැදින්",
+            "බක්",
+            "වෙසක්",
+            "පොසොන්",
+            "ඇසළ",
+            "නිකිණි",
+            "බිනර",
+            "වප්",
+            "ඉල්",
+            "උඳුවප්",
+        ],
+        [
+            "දු", "න", "මැ", "බ", "වෙ", "පො", "ඇ", "නි", "බි", "ව", "ඉ", "උ",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ඉරිදා", "සඳුදා", "අඟහරුවාදා", "බදාදා", "බ්‍රහස්පතින්දා", "සිකුරාදා", "සෙනසුරාදා"],
+        ["ඉරිදා", "සඳුදා", "අඟහ", "බදාදා", "බ්‍රහස්", "සිකු", "සෙන"],
+        ["ඉරි", "සඳු", "අඟ", "බදා", "බ්‍රහ", "සිකු", "සෙන"],
+        ["ඉ", "ස", "අ", "බ", "බ්‍ර", "සි", "සෙ"],
+    ),
+    day_periods: Locale::new_day_periods(["පෙ.ව.", "ප.ව."], ["පෙ.ව.", "ප.ව."], ["පෙ", "ප"]),
+    eras: Locale::new_eras("ක්‍රි.පූ.", "ක්‍රි.ව."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sid` locale.
+pub static SID: Locale<'static> = Locale {
+    lang: "sid",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sk` locale.
+pub static SK: Locale<'static> = Locale {
+    lang: "sk",
+    dates: Locale::new_patterns("EEEE d. MMMM y", "d. MMMM y", "d. M. y", "d. M. y"),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januára",
+            "februára",
+            "marca",
+            "apríla",
+            "mája",
+            "júna",
+            "júla",
+            "augusta",
+            "septembra",
+            "októbra",
+            "novembra",
+            "decembra",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "máj", "jún", "júl", "aug", "sep", "okt", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedeľa", "pondelok", "utorok", "streda", "štvrtok", "piatok", "sobota",
+        ],
+        ["ne", "po", "ut", "st", "št", "pi", "so"],
+        ["ne", "po", "ut", "st", "št", "pi", "so"],
+        ["n", "p", "u", "s", "š", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("pred Kr.", "po Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `skr` locale.
+pub static SKR: Locale<'static> = Locale {
+    lang: "skr",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sl` locale.
+pub static SL: Locale<'static> = Locale {
+    lang: "sl",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d. MMM y", "d. M. y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "marec",
+            "april",
+            "maj",
+            "junij",
+            "julij",
+            "avgust",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mar.", "apr.", "maj", "jun.", "jul.", "avg.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedelja",
+            "ponedeljek",
+            "torek",
+            "sreda",
+            "četrtek",
+            "petek",
+            "sobota",
+        ],
+        ["ned.", "pon.", "tor.", "sre.", "čet.", "pet.", "sob."],
+        ["ned.", "pon.", "tor.", "sre.", "čet.", "pet.", "sob."],
+        ["n", "p", "t", "s", "č", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["dop.", "pop."], ["dop.", "pop."], ["d", "p"]),
+    eras: Locale::new_eras("pr. Kr.", "po Kr."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sma` locale.
+pub static SMA: Locale<'static> = Locale {
+    lang: "sma",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sma-NO` locale.
+pub static SMA_NO: Locale<'static> = Locale {
+    lang: "sma-NO",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `smj` locale.
+pub static SMJ: Locale<'static> = Locale {
+    lang: "smj",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `smj-NO` locale.
+pub static SMJ_NO: Locale<'static> = Locale {
+    lang: "smj-NO",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `smn` locale.
+pub static SMN: Locale<'static> = Locale {
+    lang: "smn",
+    dates: Locale::new_patterns("cccc, MMMM d. y", "MMMM d. y", "MMM d. y", "d.M.y"),
+    times: Locale::new_patterns("H.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "uđđâivemáánu",
+            "kuovâmáánu",
+            "njuhčâmáánu",
+            "cuáŋuimáánu",
+            "vyesimáánu",
+            "kesimáánu",
+            "syeinimáánu",
+            "porgemáánu",
+            "čohčâmáánu",
+            "roovvâdmáánu",
+            "skammâmáánu",
+            "juovlâmáánu",
+        ],
+        [
+            "uđiv", "kuovâ", "njuhčâ", "cuáŋui", "vyesi", "kesi", "syeini", "porge", "čohčâ",
+            "roovvâd", "skammâ", "juovlâ",
+        ],
+        ["U", "K", "NJ", "C", "V", "K", "S", "P", "Č", "R", "S", "J"],
+    ),
+    days: Locale::new_days(
+        [
+            "pasepeeivi",
+            "vuossaargâ",
+            "majebaargâ",
+            "koskoho",
+            "tuorâstuv",
+            "vástuppeeivi",
+            "lávurduv",
+        ],
+        ["pas", "vuo", "maj", "kos", "tuo", "vás", "láv"],
+        ["pa", "vu", "ma", "ko", "tu", "vá", "lá"],
+        ["p", "V", "M", "K", "T", "V", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["ip.", "ep."], ["ip.", "ep."], ["ip.", "ep."]),
+    eras: Locale::new_eras("oKr.", "mKr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sms` locale.
+pub static SMS: Locale<'static> = Locale {
+    lang: "sms",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sn` locale.
+pub static SN: Locale<'static> = Locale {
+    lang: "sn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ndira",
+            "Kukadzi",
+            "Kurume",
+            "Kubvumbi",
+            "Chivabvu",
+            "Chikumi",
+            "Chikunguru",
+            "Nyamavhuvhu",
+            "Gunyana",
+            "Gumiguru",
+            "Mbudzi",
+            "Zvita",
+        ],
+        [
+            "Ndi", "Kuk", "Kur", "Kub", "Chv", "Chk", "Chg", "Nya", "Gun", "Gum", "Mbu", "Zvi",
+        ],
+        ["N", "K", "K", "K", "C", "C", "C", "N", "G", "G", "M", "Z"],
+    ),
+    days: Locale::new_days(
+        [
+            "Svondo", "Muvhuro", "Chipiri", "Chitatu", "China", "Chishanu", "Mugovera",
+        ],
+        ["Svo", "Muv", "Chp", "Cht", "Chn", "Chs", "Mug"],
+        ["Sv", "Mu", "Cp", "Ct", "Cn", "Cs", "Mg"],
+        ["S", "M", "C", "C", "C", "C", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `so` locale.
+pub static SO: Locale<'static> = Locale {
+    lang: "so",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "dd-MMM-y", "dd/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janaayo",
+            "Febraayo",
+            "Maarso",
+            "Abriil",
+            "Maayo",
+            "Juun",
+            "Luulyo",
+            "Agosto",
+            "Sebtembar",
+            "Oktoobar",
+            "Noofeembar",
+            "Diseembar",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "May", "Jun", "Lul", "Ogs", "Seb", "Okt", "Nof", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "L", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Axad", "Isniin", "Talaado", "Arbaco", "Khamiis", "Jimco", "Sabti",
+        ],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["A", "I", "T", "A", "Kh", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["GH", "GD"], ["GH", "GD"], ["h", "d"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `so-DJ` locale.
+pub static SO_DJ: Locale<'static> = Locale {
+    lang: "so-DJ",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "dd-MMM-y", "dd/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janaayo",
+            "Febraayo",
+            "Maarso",
+            "Abriil",
+            "Maayo",
+            "Juun",
+            "Luulyo",
+            "Agosto",
+            "Sebtembar",
+            "Oktoobar",
+            "Noofeembar",
+            "Diseembar",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "May", "Jun", "Lul", "Ogs", "Seb", "Okt", "Nof", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "L", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Axad", "Isniin", "Talaado", "Arbaco", "Khamiis", "Jimco", "Sabti",
+        ],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["A", "I", "T", "A", "Kh", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["GH", "GD"], ["GH", "GD"], ["h", "d"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `so-ET` locale.
+pub static SO_ET: Locale<'static> = Locale {
+    lang: "so-ET",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "dd-MMM-y", "dd/MM/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janaayo",
+            "Febraayo",
+            "Maarso",
+            "Abriil",
+            "Maayo",
+            "Juun",
+            "Luulyo",
+            "Agosto",
+            "Sebtembar",
+            "Oktoobar",
+            "Noofeembar",
+            "Diseembar",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "May", "Jun", "Lul", "Ogs", "Seb", "Okt", "Nof", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "L", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Axad", "Isniin", "Talaado", "Arbaco", "Khamiis", "Jimco", "Sabti",
+        ],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["A", "I", "T", "A", "Kh", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["GH", "GD"], ["GH", "GD"], ["h", "d"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `so-KE` locale.
+pub static SO_KE: Locale<'static> = Locale {
+    lang: "so-KE",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "dd-MMM-y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janaayo",
+            "Febraayo",
+            "Maarso",
+            "Abriil",
+            "Maayo",
+            "Juun",
+            "Luulyo",
+            "Agosto",
+            "Sebtembar",
+            "Oktoobar",
+            "Noofeembar",
+            "Diseembar",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Abr", "May", "Jun", "Lul", "Ogs", "Seb", "Okt", "Nof", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "L", "O", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Axad", "Isniin", "Talaado", "Arbaco", "Khamiis", "Jimco", "Sabti",
+        ],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["Axd", "Isn", "Tldo", "Arbc", "Khms", "Jmc", "Sbti"],
+        ["A", "I", "T", "A", "Kh", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["GH", "GD"], ["GH", "GD"], ["h", "d"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sq` locale.
+pub static SQ: Locale<'static> = Locale {
+    lang: "sq",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d.M.yy"),
+    times: Locale::new_patterns("h:mm:ss a, zzzz", "h:mm:ss a, z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janar", "shkurt", "mars", "prill", "maj", "qershor", "korrik", "gusht", "shtator",
+            "tetor", "nëntor", "dhjetor",
+        ],
+        [
+            "jan", "shk", "mar", "pri", "maj", "qer", "korr", "gush", "sht", "tet", "nën", "dhj",
+        ],
+        [
+            "j", "sh", "m", "p", "m", "q", "k", "g", "sh", "t", "n", "dh",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "e diel",
+            "e hënë",
+            "e martë",
+            "e mërkurë",
+            "e enjte",
+            "e premte",
+            "e shtunë",
+        ],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["d", "h", "m", "m", "e", "p", "sh"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["e paradites", "e pasdites"],
+        ["p.d.", "m.d."],
+        ["p.d.", "m.d."],
+    ),
+    eras: Locale::new_eras("p.K.", "mb.K."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sq-MK` locale.
+pub static SQ_MK: Locale<'static> = Locale {
+    lang: "sq-MK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d.M.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janar", "shkurt", "mars", "prill", "maj", "qershor", "korrik", "gusht", "shtator",
+            "tetor", "nëntor", "dhjetor",
+        ],
+        [
+            "jan", "shk", "mar", "pri", "maj", "qer", "korr", "gush", "sht", "tet", "nën", "dhj",
+        ],
+        [
+            "j", "sh", "m", "p", "m", "q", "k", "g", "sh", "t", "n", "dh",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "e diel",
+            "e hënë",
+            "e martë",
+            "e mërkurë",
+            "e enjte",
+            "e premte",
+            "e shtunë",
+        ],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["d", "h", "m", "m", "e", "p", "sh"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["e paradites", "e pasdites"],
+        ["p.d.", "m.d."],
+        ["p.d.", "m.d."],
+    ),
+    eras: Locale::new_eras("p.K.", "mb.K."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sq-XK` locale.
+pub static SQ_XK: Locale<'static> = Locale {
+    lang: "sq-XK",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d.M.yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "janar", "shkurt", "mars", "prill", "maj", "qershor", "korrik", "gusht", "shtator",
+            "tetor", "nëntor", "dhjetor",
+        ],
+        [
+            "jan", "shk", "mar", "pri", "maj", "qer", "korr", "gush", "sht", "tet", "nën", "dhj",
+        ],
+        [
+            "j", "sh", "m", "p", "m", "q", "k", "g", "sh", "t", "n", "dh",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "e diel",
+            "e hënë",
+            "e martë",
+            "e mërkurë",
+            "e enjte",
+            "e premte",
+            "e shtunë",
+        ],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["die", "hën", "mar", "mër", "enj", "pre", "sht"],
+        ["d", "h", "m", "m", "e", "p", "sh"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["e paradites", "e pasdites"],
+        ["p.d.", "m.d."],
+        ["p.d.", "m.d."],
+    ),
+    eras: Locale::new_eras("p.K.", "mb.K."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sr` locale.
+pub static SR: Locale<'static> = Locale {
+    lang: "sr",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јун",
+            "јул",
+            "август",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "мар", "апр", "мај", "јун", "јул", "авг", "сеп", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недеља",
+            "понедељак",
+            "уторак",
+            "среда",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сре", "чет", "пет", "суб"],
+        ["не", "по", "ут", "ср", "че", "пе", "су"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Cyrl` locale.
+pub static SR_CYRL: Locale<'static> = Locale {
+    lang: "sr-Cyrl",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јун",
+            "јул",
+            "август",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "мар", "апр", "мај", "јун", "јул", "авг", "сеп", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недеља",
+            "понедељак",
+            "уторак",
+            "среда",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сре", "чет", "пет", "суб"],
+        ["не", "по", "ут", "ср", "че", "пе", "су"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Cyrl-BA` locale.
+pub static SR_CYRL_BA: Locale<'static> = Locale {
+    lang: "sr-Cyrl-BA",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јун",
+            "јул",
+            "август",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "мар", "апр", "мај", "јун", "јул", "авг", "сеп", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недјеља",
+            "понедјељак",
+            "уторак",
+            "сриједа",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сри", "чет", "пет", "суб"],
+        ["не", "по", "ут", "ср", "че", "пе", "су"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["прије подне", "по подне"],
+        ["прије подне", "по подне"],
+        ["прије подне", "по подне"],
+    ),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Cyrl-ME` locale.
+pub static SR_CYRL_ME: Locale<'static> = Locale {
+    lang: "sr-Cyrl-ME",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јун",
+            "јул",
+            "август",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "март", "апр", "мај", "јун", "јул", "авг", "септ", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недјеља",
+            "понедељак",
+            "уторак",
+            "сриједа",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сре", "чет", "пет", "суб"],
+        ["не", "по", "ут", "ср", "че", "пе", "су"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["прије подне", "по подне"],
+        ["прије подне", "по подне"],
+        ["прије подне", "по подне"],
+    ),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Cyrl-XK` locale.
+pub static SR_CYRL_XK: Locale<'static> = Locale {
+    lang: "sr-Cyrl-XK",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "јануар",
+            "фебруар",
+            "март",
+            "април",
+            "мај",
+            "јун",
+            "јул",
+            "август",
+            "септембар",
+            "октобар",
+            "новембар",
+            "децембар",
+        ],
+        [
+            "јан", "феб", "март", "апр", "мај", "јун", "јул", "авг", "септ", "окт", "нов", "дец",
+        ],
+        ["ј", "ф", "м", "а", "м", "ј", "ј", "а", "с", "о", "н", "д"],
+    ),
+    days: Locale::new_days(
+        [
+            "недеља",
+            "понедељак",
+            "уторак",
+            "среда",
+            "четвртак",
+            "петак",
+            "субота",
+        ],
+        ["нед", "пон", "уто", "сре", "чет", "пет", "суб"],
+        ["не", "по", "ут", "ср", "че", "пе", "су"],
+        ["н", "п", "у", "с", "ч", "п", "с"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("п. н. е.", "н. е."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Latn` locale.
+pub static SR_LATN: Locale<'static> = Locale {
+    lang: "sr-Latn",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "jun",
+            "jul",
+            "avgust",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "maj", "jun", "jul", "avg", "sep", "okt", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedelja",
+            "ponedeljak",
+            "utorak",
+            "sreda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sre", "čet", "pet", "sub"],
+        ["ne", "po", "ut", "sr", "če", "pe", "su"],
+        ["n", "p", "u", "s", "č", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Latn-BA` locale.
+pub static SR_LATN_BA: Locale<'static> = Locale {
+    lang: "sr-Latn-BA",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "jun",
+            "jul",
+            "avgust",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mar", "apr", "maj", "jun", "jul", "avg", "sep", "okt", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedjeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sri", "čet", "pet", "sub"],
+        ["ne", "po", "ut", "sr", "če", "pe", "su"],
+        ["n", "p", "u", "s", "č", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["prije podne", "po podne"],
+        ["prije podne", "po podne"],
+        ["prije podne", "po podne"],
+    ),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Latn-ME` locale.
+pub static SR_LATN_ME: Locale<'static> = Locale {
+    lang: "sr-Latn-ME",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "jun",
+            "jul",
+            "avgust",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mart", "apr", "maj", "jun", "jul", "avg", "sept", "okt", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedjelja",
+            "ponedeljak",
+            "utorak",
+            "srijeda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sre", "čet", "pet", "sub"],
+        ["ne", "po", "ut", "sr", "če", "pe", "su"],
+        ["n", "p", "u", "s", "č", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["prije podne", "po podne"],
+        ["prije podne", "po podne"],
+        ["prije podne", "po podne"],
+    ),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sr-Latn-XK` locale.
+pub static SR_LATN_XK: Locale<'static> = Locale {
+    lang: "sr-Latn-XK",
+    dates: Locale::new_patterns("EEEE, d. MMMM y.", "d. MMMM y.", "d. M. y.", "d. M. y."),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januar",
+            "februar",
+            "mart",
+            "april",
+            "maj",
+            "jun",
+            "jul",
+            "avgust",
+            "septembar",
+            "oktobar",
+            "novembar",
+            "decembar",
+        ],
+        [
+            "jan", "feb", "mart", "apr", "maj", "jun", "jul", "avg", "sept", "okt", "nov", "dec",
+        ],
+        ["j", "f", "m", "a", "m", "j", "j", "a", "s", "o", "n", "d"],
+    ),
+    days: Locale::new_days(
+        [
+            "nedelja",
+            "ponedeljak",
+            "utorak",
+            "sreda",
+            "četvrtak",
+            "petak",
+            "subota",
+        ],
+        ["ned", "pon", "uto", "sre", "čet", "pet", "sub"],
+        ["ne", "po", "ut", "sr", "če", "pe", "su"],
+        ["n", "p", "u", "s", "č", "p", "s"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("p. n. e.", "n. e."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `ss` locale.
+pub static SS: Locale<'static> = Locale {
+    lang: "ss",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ss-SZ` locale.
+pub static SS_SZ: Locale<'static> = Locale {
+    lang: "ss-SZ",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ssy` locale.
+pub static SSY: Locale<'static> = Locale {
+    lang: "ssy",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `st` locale.
+pub static ST: Locale<'static> = Locale {
+    lang: "st",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Pherekgong",
+            "Hlakola",
+            "Hlakubele",
+            "Mmesa",
+            "Motsheanong",
+            "Phupjane",
+            "Phupu",
+            "Phato",
+            "Lwetse",
+            "Mphalane",
+            "Pudungwana",
+            "Tshitwe",
+        ],
+        [
+            "Phe", "Kol", "Hlb", "Mme", "Mot", "Jan", "Upu", "Pha", "Leo", "Mph", "Pun", "Tsh",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sontaha",
+            "Mantaha",
+            "Labobedi",
+            "Laboraro",
+            "Labone",
+            "Labohlano",
+            "Moqebelo",
+        ],
+        ["Son", "Mma", "Bed", "Rar", "Ne", "Hla", "Moq"],
+        ["Son", "Mma", "Bed", "Rar", "Ne", "Hla", "Moq"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["Hoseng", "Thapama"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `st-LS` locale.
+pub static ST_LS: Locale<'static> = Locale {
+    lang: "st-LS",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Pherekgong",
+            "Hlakola",
+            "Hlakubele",
+            "Mmesa",
+            "Motsheanong",
+            "Phupjane",
+            "Phupu",
+            "Phato",
+            "Lwetse",
+            "Mphalane",
+            "Pudungwana",
+            "Tshitwe",
+        ],
+        [
+            "Phe", "Kol", "Hlb", "Mme", "Mot", "Jan", "Upu", "Pha", "Leo", "Mph", "Pun", "Tsh",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sontaha",
+            "Mantaha",
+            "Labobedi",
+            "Laboraro",
+            "Labone",
+            "Labohlano",
+            "Moqebelo",
+        ],
+        ["Son", "Mma", "Bed", "Rar", "Ne", "Hla", "Moq"],
+        ["Son", "Mma", "Bed", "Rar", "Ne", "Hla", "Moq"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["Hoseng", "Thapama"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `su` locale.
+pub static SU: Locale<'static> = Locale {
+    lang: "su",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("H.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Pébruari",
+            "Maret",
+            "April",
+            "Méi",
+            "Juni",
+            "Juli",
+            "Agustus",
+            "Séptémber",
+            "Oktober",
+            "Nopémber",
+            "Désémber",
+        ],
+        [
+            "Jan", "Péb", "Mar", "Apr", "Méi", "Jun", "Jul", "Ags", "Sép", "Okt", "Nop", "Dés",
+        ],
+        ["J", "P", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Minggu", "Senén", "Salasa", "Rebo", "Kemis", "Jumaah", "Saptu",
+        ],
+        ["Mng", "Sen", "Sal", "Reb", "Kem", "Jum", "Sap"],
+        ["Mng", "Sen", "Sal", "Reb", "Kem", "Jum", "Sap"],
+        ["M", "S", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("SM", "M"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `su-Latn` locale.
+pub static SU_LATN: Locale<'static> = Locale {
+    lang: "su-Latn",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("H.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Januari",
+            "Pébruari",
+            "Maret",
+            "April",
+            "Méi",
+            "Juni",
+            "Juli",
+            "Agustus",
+            "Séptémber",
+            "Oktober",
+            "Nopémber",
+            "Désémber",
+        ],
+        [
+            "Jan", "Péb", "Mar", "Apr", "Méi", "Jun", "Jul", "Ags", "Sép", "Okt", "Nop", "Dés",
+        ],
+        ["J", "P", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Minggu", "Senén", "Salasa", "Rebo", "Kemis", "Jumaah", "Saptu",
+        ],
+        ["Mng", "Sen", "Sal", "Reb", "Kem", "Jum", "Sap"],
+        ["Mng", "Sen", "Sal", "Reb", "Kem", "Jum", "Sap"],
+        ["M", "S", "S", "R", "K", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("SM", "M"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `suz` locale.
+pub static SUZ: Locale<'static> = Locale {
+    lang: "suz",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `suz-Deva` locale.
+pub static SUZ_DEVA: Locale<'static> = Locale {
+    lang: "suz-Deva",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `suz-Sunu` locale.
+pub static SUZ_SUNU: Locale<'static> = Locale {
+    lang: "suz-Sunu",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sv` locale.
+pub static SV: Locale<'static> = Locale {
+    lang: "sv",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "mars",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "augusti",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "maj", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "söndag", "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag",
+        ],
+        ["sön", "mån", "tis", "ons", "tors", "fre", "lör"],
+        ["sö", "må", "ti", "on", "to", "fr", "lö"],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["fm", "em"], ["fm", "em"], ["fm", "em"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sv-AX` locale.
+pub static SV_AX: Locale<'static> = Locale {
+    lang: "sv-AX",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d.M.y", "d.M.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "mars",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "augusti",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "maj", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "söndag", "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag",
+        ],
+        ["sön", "mån", "tis", "ons", "tors", "fre", "lör"],
+        ["sö", "må", "ti", "on", "to", "fr", "lö"],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["fm", "em"], ["fm", "em"], ["fm", "em"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sv-FI` locale.
+pub static SV_FI: Locale<'static> = Locale {
+    lang: "sv-FI",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d.M.y"),
+    times: Locale::new_patterns("HH.mm.ss zzzz", "H.mm.ss z", "H.mm.ss", "H.mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "januari",
+            "februari",
+            "mars",
+            "april",
+            "maj",
+            "juni",
+            "juli",
+            "augusti",
+            "september",
+            "oktober",
+            "november",
+            "december",
+        ],
+        [
+            "jan.", "feb.", "mars", "apr.", "maj", "juni", "juli", "aug.", "sep.", "okt.", "nov.",
+            "dec.",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "söndag", "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag",
+        ],
+        ["sön", "mån", "tis", "ons", "tors", "fre", "lör"],
+        ["sö", "må", "ti", "on", "to", "fr", "lö"],
+        ["S", "M", "T", "O", "T", "F", "L"],
+    ),
+    day_periods: Locale::new_day_periods(["fm", "em"], ["fm", "em"], ["fm", "em"]),
+    eras: Locale::new_eras("f.Kr.", "e.Kr."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `sw` locale.
+pub static SW: Locale<'static> = Locale {
+    lang: "sw",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sw-CD` locale.
+pub static SW_CD: Locale<'static> = Locale {
+    lang: "sw-CD",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `sw-KE` locale.
+pub static SW_KE: Locale<'static> = Locale {
+    lang: "sw-KE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `sw-UG` locale.
+pub static SW_UG: Locale<'static> = Locale {
+    lang: "sw-UG",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprili", "Mei", "Juni", "Julai", "Agosti", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        [
+            "Jumapili", "Jumatatu", "Jumanne", "Jumatano", "Alhamisi", "Ijumaa", "Jumamosi",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["am", "pm"]),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `syr` locale.
+pub static SYR: Locale<'static> = Locale {
+    lang: "syr",
+    dates: Locale::new_patterns("EEEE، d ܒMMMM y", "d ܒMMMM y", "d ܒMMM y", "d-MM-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "ܟܢܘܢ ܐܚܪܝܐ",
+            "ܫܒܛ",
+            "ܐܕܪ",
+            "ܢܝܣܢ",
+            "ܐܝܪ",
+            "ܚܙܝܪܢ",
+            "ܬܡܘܙ",
+            "ܐܒ",
+            "ܐܝܠܘܠ",
+            "ܬܫܪܝܢ ܩܕܡܝܐ",
+            "ܬܫܪܝܢ ܐܚܪܝܐ",
+            "ܟܢܘܢ ܩܕܡܝܐ",
+        ],
+        [
+            "ܟܢܘܢ ܒ",
+            "ܫܒܛ",
+            "ܐܕܪ",
+            "ܢܝܣܢ",
+            "ܐܝܪ",
+            "ܚܙܝܪܢ",
+            "ܬܡܘܙ",
+            "ܐܒ",
+            "ܐܝܠܘܠ",
+            "ܬܫܪܝܢ ܐ",
+            "ܬܫܪܝܢ ܒ",
+            "ܟܢܘܢ ܐ",
+        ],
+        ["ܟ", "ܫ", "ܐ", "ܢ", "ܐ", "ܚ", "ܬ", "ܐ", "ܐ", "ܬ", "ܬ", "ܟ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ܚܕܒܫܒܐ",
+            "ܬܪܝܢܒܫܒܐ",
+            "ܬܠܬܒܫܒܐ",
+            "ܐܪܒܥܒܫܒܐ",
+            "ܚܡܫܒܫܒܐ",
+            "ܥܪܘܒܬܐ",
+            "ܫܒܬܐ",
+        ],
+        ["ܚܕ", "ܬܪܝܢ", "ܬܠܬ", "ܐܪܒܥ", "ܚܡܫ", "ܥܪܘ", "ܫܒܬܐ"],
+        ["ܚܕ", "ܬܪܝܢ", "ܬܠܬ", "ܐܪܒܥ", "ܚܡܫ", "ܥܪܘ", "ܫܒܬܐ"],
+        ["ܚ", "ܬ", "ܬ", "ܐ", "ܚ", "ܥ", "ܫ"],
+    ),
+    day_periods: Locale::new_day_periods(["܏ܩܛ‌", "܏ܒܛ‌"], ["܏ܩܛ‌", "܏ܒܛ‌"], ["܏ܩ‌", "܏ܒ‌"]),
+    eras: Locale::new_eras("܏ܩܡ‌", "܏ܫܡ‌"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `syr-SY` locale.
+pub static SYR_SY: Locale<'static> = Locale {
+    lang: "syr-SY",
+    dates: Locale::new_patterns("EEEE، d ܒMMMM y", "d ܒMMMM y", "d ܒMMM y", "d-MM-y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}، {0}", "{1}، {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "ܟܢܘܢ ܐܚܪܝܐ",
+            "ܫܒܛ",
+            "ܐܕܪ",
+            "ܢܝܣܢ",
+            "ܐܝܪ",
+            "ܚܙܝܪܢ",
+            "ܬܡܘܙ",
+            "ܐܒ",
+            "ܐܝܠܘܠ",
+            "ܬܫܪܝܢ ܩܕܡܝܐ",
+            "ܬܫܪܝܢ ܐܚܪܝܐ",
+            "ܟܢܘܢ ܩܕܡܝܐ",
+        ],
+        [
+            "ܟܢܘܢ ܒ",
+            "ܫܒܛ",
+            "ܐܕܪ",
+            "ܢܝܣܢ",
+            "ܐܝܪ",
+            "ܚܙܝܪܢ",
+            "ܬܡܘܙ",
+            "ܐܒ",
+            "ܐܝܠܘܠ",
+            "ܬܫܪܝܢ ܐ",
+            "ܬܫܪܝܢ ܒ",
+            "ܟܢܘܢ ܐ",
+        ],
+        ["ܟ", "ܫ", "ܐ", "ܢ", "ܐ", "ܚ", "ܬ", "ܐ", "ܐ", "ܬ", "ܬ", "ܟ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ܚܕܒܫܒܐ",
+            "ܬܪܝܢܒܫܒܐ",
+            "ܬܠܬܒܫܒܐ",
+            "ܐܪܒܥܒܫܒܐ",
+            "ܚܡܫܒܫܒܐ",
+            "ܥܪܘܒܬܐ",
+            "ܫܒܬܐ",
+        ],
+        ["ܚܕ", "ܬܪܝܢ", "ܬܠܬ", "ܐܪܒܥ", "ܚܡܫ", "ܥܪܘ", "ܫܒܬܐ"],
+        ["ܚܕ", "ܬܪܝܢ", "ܬܠܬ", "ܐܪܒܥ", "ܚܡܫ", "ܥܪܘ", "ܫܒܬܐ"],
+        ["ܚ", "ܬ", "ܬ", "ܐ", "ܚ", "ܥ", "ܫ"],
+    ),
+    day_periods: Locale::new_day_periods(["܏ܩܛ‌", "܏ܒܛ‌"], ["܏ܩܛ‌", "܏ܒܛ‌"], ["܏ܩ‌", "܏ܒ‌"]),
+    eras: Locale::new_eras("܏ܩܡ‌", "܏ܫܡ‌"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `szl` locale.
+pub static SZL: Locale<'static> = Locale {
+    lang: "szl",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "stycznia",
+            "lutego",
+            "marca",
+            "kwietnia",
+            "moja",
+            "czyrwca",
+            "lipca",
+            "siyrpnia",
+            "września",
+            "października",
+            "listopada",
+            "grudnia",
+        ],
+        [
+            "stycznia",
+            "lutego",
+            "marca",
+            "kwietnia",
+            "moja",
+            "czyrwca",
+            "lipca",
+            "siyrpnia",
+            "września",
+            "października",
+            "listopada",
+            "grudnia",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "niydziela",
+            "pyńdziałek",
+            "wtorek",
+            "strzoda",
+            "sztwortek",
+            "piōntek",
+            "sobota",
+        ],
+        [
+            "niydziela",
+            "pyńdziałek",
+            "wtorek",
+            "strzoda",
+            "sztwortek",
+            "piōntek",
+            "sobota",
+        ],
+        [
+            "niydziela",
+            "pyńdziałek",
+            "wtorek",
+            "strzoda",
+            "sztwortek",
+            "piōntek",
+            "sobota",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["do połedniŏ", "po połedniu"],
+        ["do połedniŏ", "po połedniu"],
+        ["do połedniŏ", "po połedniu"],
+    ),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ta` locale.
+pub static TA: Locale<'static> = Locale {
+    lang: "ta",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ஜனவரி",
+            "பிப்ரவரி",
+            "மார்ச்",
+            "ஏப்ரல்",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆகஸ்ட்",
+            "செப்டம்பர்",
+            "அக்டோபர்",
+            "நவம்பர்",
+            "டிசம்பர்",
+        ],
+        [
+            "ஜன.",
+            "பிப்.",
+            "மார்.",
+            "ஏப்.",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆக.",
+            "செப்.",
+            "அக்.",
+            "நவ.",
+            "டிச.",
+        ],
+        [
+            "ஜ", "பி", "மா", "ஏ", "மே", "ஜூ", "ஜூ", "ஆ", "செ", "அ", "ந", "டி",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+        ["ஞாயி.", "திங்.", "செவ்.", "புத.", "வியா.", "வெள்.", "சனி"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("கி.மு.", "கி.பி."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ta-LK` locale.
+pub static TA_LK: Locale<'static> = Locale {
+    lang: "ta-LK",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ஜனவரி",
+            "பிப்ரவரி",
+            "மார்ச்",
+            "ஏப்ரல்",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆகஸ்ட்",
+            "செப்டம்பர்",
+            "அக்டோபர்",
+            "நவம்பர்",
+            "டிசம்பர்",
+        ],
+        [
+            "ஜன.",
+            "பிப்.",
+            "மார்.",
+            "ஏப்.",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆக.",
+            "செப்.",
+            "அக்.",
+            "நவ.",
+            "டிச.",
+        ],
+        [
+            "ஜ", "பி", "மா", "ஏ", "மே", "ஜூ", "ஜூ", "ஆ", "செ", "அ", "ந", "டி",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+        ["ஞாயி.", "திங்.", "செவ்.", "புத.", "வியா.", "வெள்.", "சனி"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("கி.மு.", "கி.பி."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ta-MY` locale.
+pub static TA_MY: Locale<'static> = Locale {
+    lang: "ta-MY",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ஜனவரி",
+            "பிப்ரவரி",
+            "மார்ச்",
+            "ஏப்ரல்",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆகஸ்ட்",
+            "செப்டம்பர்",
+            "அக்டோபர்",
+            "நவம்பர்",
+            "டிசம்பர்",
+        ],
+        [
+            "ஜன.",
+            "பிப்.",
+            "மார்.",
+            "ஏப்.",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆக.",
+            "செப்.",
+            "அக்.",
+            "நவ.",
+            "டிச.",
+        ],
+        [
+            "ஜ", "பி", "மா", "ஏ", "மே", "ஜூ", "ஜூ", "ஆ", "செ", "அ", "ந", "டி",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+        ["ஞாயி.", "திங்.", "செவ்.", "புத.", "வியா.", "வெள்.", "சனி"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("கி.மு.", "கி.பி."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ta-SG` locale.
+pub static TA_SG: Locale<'static> = Locale {
+    lang: "ta-SG",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "ஜனவரி",
+            "பிப்ரவரி",
+            "மார்ச்",
+            "ஏப்ரல்",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆகஸ்ட்",
+            "செப்டம்பர்",
+            "அக்டோபர்",
+            "நவம்பர்",
+            "டிசம்பர்",
+        ],
+        [
+            "ஜன.",
+            "பிப்.",
+            "மார்.",
+            "ஏப்.",
+            "மே",
+            "ஜூன்",
+            "ஜூலை",
+            "ஆக.",
+            "செப்.",
+            "அக்.",
+            "நவ.",
+            "டிச.",
+        ],
+        [
+            "ஜ", "பி", "மா", "ஏ", "மே", "ஜூ", "ஜூ", "ஆ", "செ", "அ", "ந", "டி",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ஞாயிறு", "திங்கள்", "செவ்வாய்", "புதன்", "வியாழன்", "வெள்ளி", "சனி"],
+        ["ஞாயி.", "திங்.", "செவ்.", "புத.", "வியா.", "வெள்.", "சனி"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+        ["ஞா", "தி", "செ", "பு", "வி", "வெ", "ச"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("கி.மு.", "கி.பி."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `te` locale.
+pub static TE: Locale<'static> = Locale {
+    lang: "te",
+    dates: Locale::new_patterns("d, MMMM y, EEEE", "d MMMM, y", "d MMM, y", "dd-MM-yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "జనవరి",
+            "ఫిబ్రవరి",
+            "మార్చి",
+            "ఏప్రిల్",
+            "మే",
+            "జూన్",
+            "జులై",
+            "ఆగస్టు",
+            "సెప్టెంబర్",
+            "అక్టోబర్",
+            "నవంబర్",
+            "డిసెంబర్",
+        ],
+        [
+            "జన",
+            "ఫిబ్ర",
+            "మార్చి",
+            "ఏప్రి",
+            "మే",
+            "జూన్",
+            "జులై",
+            "ఆగ",
+            "సెప్టెం",
+            "అక్టో",
+            "నవం",
+            "డిసెం",
+        ],
+        ["జ", "ఫి", "మా", "ఏ", "మే", "జూ", "జు", "ఆ", "సె", "అ", "న", "డి"],
+    ),
+    days: Locale::new_days(
+        [
+            "ఆదివారం",
+            "సోమవారం",
+            "మంగళవారం",
+            "బుధవారం",
+            "గురువారం",
+            "శుక్రవారం",
+            "శనివారం",
+        ],
+        ["ఆది", "సోమ", "మంగళ", "బుధ", "గురు", "శుక్ర", "శని"],
+        ["ఆది", "సోమ", "మం", "బుధ", "గురు", "శుక్ర", "శని"],
+        ["ఆ", "సో", "మ", "బు", "గు", "శు", "శ"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("క్రీపూ", "క్రీశ"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `teo` locale.
+pub static TEO: Locale<'static> = Locale {
+    lang: "teo",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Orara",
+            "Omuk",
+            "Okwamg’",
+            "Odung’el",
+            "Omaruk",
+            "Omodok’king’ol",
+            "Ojola",
+            "Opedel",
+            "Osokosokoma",
+            "Otibar",
+            "Olabor",
+            "Opoo",
+        ],
+        [
+            "Rar", "Muk", "Kwa", "Dun", "Mar", "Mod", "Jol", "Ped", "Sok", "Tib", "Lab", "Poo",
+        ],
+        ["R", "M", "K", "D", "M", "M", "J", "P", "S", "T", "L", "P"],
+    ),
+    days: Locale::new_days(
+        [
+            "Nakaejuma",
+            "Nakaebarasa",
+            "Nakaare",
+            "Nakauni",
+            "Nakaung’on",
+            "Nakakany",
+            "Nakasabiti",
+        ],
+        ["Jum", "Bar", "Aar", "Uni", "Ung", "Kan", "Sab"],
+        ["Jum", "Bar", "Aar", "Uni", "Ung", "Kan", "Sab"],
+        ["J", "B", "A", "U", "U", "K", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Taparachu", "Ebongi"],
+        ["Taparachu", "Ebongi"],
+        ["Taparachu", "Ebongi"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `teo-KE` locale.
+pub static TEO_KE: Locale<'static> = Locale {
+    lang: "teo-KE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Orara",
+            "Omuk",
+            "Okwamg’",
+            "Odung’el",
+            "Omaruk",
+            "Omodok’king’ol",
+            "Ojola",
+            "Opedel",
+            "Osokosokoma",
+            "Otibar",
+            "Olabor",
+            "Opoo",
+        ],
+        [
+            "Rar", "Muk", "Kwa", "Dun", "Mar", "Mod", "Jol", "Ped", "Sok", "Tib", "Lab", "Poo",
+        ],
+        ["R", "M", "K", "D", "M", "M", "J", "P", "S", "T", "L", "P"],
+    ),
+    days: Locale::new_days(
+        [
+            "Nakaejuma",
+            "Nakaebarasa",
+            "Nakaare",
+            "Nakauni",
+            "Nakaung’on",
+            "Nakakany",
+            "Nakasabiti",
+        ],
+        ["Jum", "Bar", "Aar", "Uni", "Ung", "Kan", "Sab"],
+        ["Jum", "Bar", "Aar", "Uni", "Ung", "Kan", "Sab"],
+        ["J", "B", "A", "U", "U", "K", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Taparachu", "Ebongi"],
+        ["Taparachu", "Ebongi"],
+        ["Taparachu", "Ebongi"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `tg` locale.
+pub static TG: Locale<'static> = Locale {
+    lang: "tg",
+    dates: Locale::new_patterns("EEEE, dd MMMM y", "dd MMMM y", "dd MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Январ",
+            "Феврал",
+            "Март",
+            "Апрел",
+            "Май",
+            "Июн",
+            "Июл",
+            "Август",
+            "Сентябр",
+            "Октябр",
+            "Ноябр",
+            "Декабр",
+        ],
+        [
+            "Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "Якшанбе",
+            "Душанбе",
+            "Сешанбе",
+            "Чоршанбе",
+            "Панҷшанбе",
+            "Ҷумъа",
+            "Шанбе",
+        ],
+        ["Яшб", "Дшб", "Сшб", "Чшб", "Пшб", "Ҷмъ", "Шнб"],
+        ["Яшб", "Дшб", "Сшб", "Чшб", "Пшб", "Ҷмъ", "Шнб"],
+        ["Я", "Д", "С", "Ч", "П", "Ҷ", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("ПеМ", "ПаМ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `th` locale.
+pub static TH: Locale<'static> = Locale {
+    lang: "th",
+    dates: Locale::new_patterns("EEEEที่ d MMMM G y", "d MMMM G y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns(
+        "H นาฬิกา mm นาที ss วินาที zzzz",
+        "H นาฬิกา mm นาที ss วินาที z",
+        "HH:mm:ss",
+        "HH:mm",
+    ),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "มกราคม",
+            "กุมภาพันธ์",
+            "มีนาคม",
+            "เมษายน",
+            "พฤษภาคม",
+            "มิถุนายน",
+            "กรกฎาคม",
+            "สิงหาคม",
+            "กันยายน",
+            "ตุลาคม",
+            "พฤศจิกายน",
+            "ธันวาคม",
+        ],
+        [
+            "ม.ค.",
+            "ก.พ.",
+            "มี.ค.",
+            "เม.ย.",
+            "พ.ค.",
+            "มิ.ย.",
+            "ก.ค.",
+            "ส.ค.",
+            "ก.ย.",
+            "ต.ค.",
+            "พ.ย.",
+            "ธ.ค.",
+        ],
+        [
+            "ม.ค.",
+            "ก.พ.",
+            "มี.ค.",
+            "เม.ย.",
+            "พ.ค.",
+            "มิ.ย.",
+            "ก.ค.",
+            "ส.ค.",
+            "ก.ย.",
+            "ต.ค.",
+            "พ.ย.",
+            "ธ.ค.",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "วันอาทิตย์",
+            "วันจันทร์",
+            "วันอังคาร",
+            "วันพุธ",
+            "วันพฤหัสบดี",
+            "วันศุกร์",
+            "วันเสาร์",
+        ],
+        ["อาทิตย์", "จันทร์", "อังคาร", "พุธ", "พฤหัส", "ศุกร์", "เสาร์"],
+        ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."],
+        ["อา", "จ", "อ", "พ", "พฤ", "ศ", "ส"],
+    ),
+    day_periods: Locale::new_day_periods(["ก่อนเที่ยง", "หลังเที่ยง"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("ก่อน ค.ศ.", "ค.ศ."),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ti` locale.
+pub static TI: Locale<'static> = Locale {
+    lang: "ti",
+    dates: Locale::new_patterns("EEEE፣ d MMMM y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ጥሪ",
+            "ለካቲት",
+            "መጋቢት",
+            "ሚያዝያ",
+            "ጉንበት",
+            "ሰነ",
+            "ሓምለ",
+            "ነሓሰ",
+            "መስከረም",
+            "ጥቅምቲ",
+            "ሕዳር",
+            "ታሕሳስ",
+        ],
+        [
+            "ጥሪ", "ለካ", "መጋ", "ሚያ", "ግን", "ሰነ", "ሓም", "ነሓ", "መስ", "ጥቅ", "ሕዳ", "ታሕ",
+        ],
+        ["ጥ", "ለ", "መ", "ሚ", "ግ", "ሰ", "ሓ", "ነ", "መ", "ጥ", "ሕ", "ታ"],
+    ),
+    days: Locale::new_days(
+        ["ሰንበት", "ሰኑይ", "ሰሉስ", "ረቡዕ", "ሓሙስ", "ዓርቢ", "ቀዳም"],
+        ["ሰን", "ሰኑ", "ሰሉ", "ረቡ", "ሓሙ", "ዓር", "ቀዳ"],
+        ["ሰን", "ሰኑ", "ሰሉ", "ረቡ", "ሓሙ", "ዓር", "ቀዳ"],
+        ["ሰ", "ሰ", "ሰ", "ረ", "ሓ", "ዓ", "ቀ"],
+    ),
+    day_periods: Locale::new_day_periods(["ቅ.ቀ.", "ድ.ቀ."], ["ቅ.ቀ.", "ድ.ቀ."], ["ቅ.ቀ.", "ድ.ቀ."]),
+    eras: Locale::new_eras("ዓ/ዓ", "ዓ/ም"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ti-ER` locale.
+pub static TI_ER: Locale<'static> = Locale {
+    lang: "ti-ER",
+    dates: Locale::new_patterns("EEEE፣ d MMMM y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ጥሪ",
+            "ለካቲት",
+            "መጋቢት",
+            "ሚያዝያ",
+            "ጉንበት",
+            "ሰነ",
+            "ሓምለ",
+            "ነሓሰ",
+            "መስከረም",
+            "ጥቅምቲ",
+            "ሕዳር",
+            "ታሕሳስ",
+        ],
+        [
+            "ጥሪ", "ለካ", "መጋ", "ሚያ", "ግን", "ሰነ", "ሓም", "ነሓ", "መስ", "ጥቅ", "ሕዳ", "ታሕ",
+        ],
+        ["ጥ", "ለ", "መ", "ሚ", "ግ", "ሰ", "ሓ", "ነ", "መ", "ጥ", "ሕ", "ታ"],
+    ),
+    days: Locale::new_days(
+        ["ሰንበት", "ሰኑይ", "ሰሉስ", "ረቡዕ", "ሓሙስ", "ዓርቢ", "ቀዳም"],
+        ["ሰን", "ሰኑ", "ሰሉ", "ረቡ", "ሓሙ", "ዓር", "ቀዳ"],
+        ["ሰን", "ሰኑ", "ሰሉ", "ረቡ", "ሓሙ", "ዓር", "ቀዳ"],
+        ["ሰ", "ሰ", "ሰ", "ረ", "ሓ", "ዓ", "ቀ"],
+    ),
+    day_periods: Locale::new_day_periods(["ቅ.ቀ.", "ድ.ቀ."], ["ቅ.ቀ.", "ድ.ቀ."], ["ቅ.ቀ.", "ድ.ቀ."]),
+    eras: Locale::new_eras("ዓ/ዓ", "ዓ/ም"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `tig` locale.
+pub static TIG: Locale<'static> = Locale {
+    lang: "tig",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `tk` locale.
+pub static TK: Locale<'static> = Locale {
+    lang: "tk",
+    dates: Locale::new_patterns("d MMMM y EEEE", "d MMMM y", "d MMM y", "dd.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ýanwar",
+            "fewral",
+            "mart",
+            "aprel",
+            "maý",
+            "iýun",
+            "iýul",
+            "awgust",
+            "sentýabr",
+            "oktýabr",
+            "noýabr",
+            "dekabr",
+        ],
+        [
+            "ýan", "few", "mart", "apr", "maý", "iýun", "iýul", "awg", "sen", "okt", "noý", "dek",
+        ],
+        ["Ý", "F", "M", "A", "M", "I", "I", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "ýekşenbe",
+            "duşenbe",
+            "sişenbe",
+            "çarşenbe",
+            "penşenbe",
+            "anna",
+            "şenbe",
+        ],
+        ["ýek", "duş", "siş", "çar", "pen", "ann", "şen"],
+        ["ýb", "db", "sb", "çb", "pb", "an", "şb"],
+        ["Ý", "D", "S", "Ç", "P", "A", "Ş"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["günortadan öň", "günortadan soň"],
+        ["go.öň", "go.soň"],
+        ["öň", "soň"],
+    ),
+    eras: Locale::new_eras("B.e.öň", "B.e."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `tn` locale.
+pub static TN: Locale<'static> = Locale {
+    lang: "tn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} 'ka' {0}", "{1} 'ka' {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ferikgong",
+            "Tlhakole",
+            "Mopitlo",
+            "Moranang",
+            "Motsheganang",
+            "Seetebosigo",
+            "Phukwi",
+            "Phatwe",
+            "Lwetse",
+            "Diphalane",
+            "Ngwanatsele",
+            "Sedimonthole",
+        ],
+        [
+            "Fer", "Tlh", "Mop", "Mor", "Mot", "See", "Phu", "Pha", "Lwe", "Dip", "Ngw", "Sed",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Tshipi",
+            "Mosupologo",
+            "Labobedi",
+            "Laboraro",
+            "Labone",
+            "Labotlhano",
+            "Matlhatso",
+        ],
+        ["Tsh", "Mos", "Labb", "Labr", "Labn", "Labt", "Mat"],
+        ["Tsh", "Mos", "Labb", "Labr", "Labn", "Labt", "Mat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `tn-BW` locale.
+pub static TN_BW: Locale<'static> = Locale {
+    lang: "tn-BW",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} 'ka' {0}", "{1} 'ka' {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Ferikgong",
+            "Tlhakole",
+            "Mopitlo",
+            "Moranang",
+            "Motsheganang",
+            "Seetebosigo",
+            "Phukwi",
+            "Phatwe",
+            "Lwetse",
+            "Diphalane",
+            "Ngwanatsele",
+            "Sedimonthole",
+        ],
+        [
+            "Fer", "Tlh", "Mop", "Mor", "Mot", "See", "Phu", "Pha", "Lwe", "Dip", "Ngw", "Sed",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Tshipi",
+            "Mosupologo",
+            "Labobedi",
+            "Laboraro",
+            "Labone",
+            "Labotlhano",
+            "Matlhatso",
+        ],
+        ["Tsh", "Mos", "Labb", "Labr", "Labn", "Labt", "Mat"],
+        ["Tsh", "Mos", "Labb", "Labr", "Labn", "Labt", "Mat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: '\'',
+};
+/// Data for `to` locale.
+pub static TO: Locale<'static> = Locale {
+    lang: "to",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Sānuali",
+            "Fēpueli",
+            "Maʻasi",
+            "ʻEpeleli",
+            "Mē",
+            "Sune",
+            "Siulai",
+            "ʻAokosi",
+            "Sēpitema",
+            "ʻOkatopa",
+            "Nōvema",
+            "Tīsema",
+        ],
+        [
+            "Sān", "Fēp", "Maʻa", "ʻEpe", "Mē", "Sun", "Siu", "ʻAok", "Sēp", "ʻOka", "Nōv", "Tīs",
+        ],
+        [
+            "S", "F", "M", "ʻE", "M", "S", "S", "ʻA", "S", "ʻO", "N", "T",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sāpate",
+            "Mōnite",
+            "Tūsite",
+            "Pulelulu",
+            "Tuʻapulelulu",
+            "Falaite",
+            "Tokonaki",
+        ],
+        ["Sāp", "Mōn", "Tūs", "Pul", "Tuʻa", "Fal", "Tok"],
+        ["Sāp", "Mōn", "Tūs", "Pul", "Tuʻa", "Fal", "Tok"],
+        ["S", "M", "T", "P", "T", "F", "T"],
+    ),
+    day_periods: Locale::new_day_periods(["hengihengi", "efiafi"], ["HH", "EA"], ["HH", "EA"]),
+    eras: Locale::new_eras("KM", "TS"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `tok` locale.
+pub static TOK: Locale<'static> = Locale {
+    lang: "tok",
+    dates: Locale::new_patterns(
+        "'sike' #y 'la' MMM 'la' 'suno' #d",
+        "'tenpo' 'sike' #y 'la' 'tenpo' MMMM 'la' 'tenpo' 'suno' #d",
+        "'sike' #y 'la' MMM 'la' 'suno' #d",
+        "y-MM-dd",
+    ),
+    times: Locale::new_patterns("zzzz 'la' HH:mm:ss", "z 'la' HH:mm:ss", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "tenpo mun #1",
+            "tenpo mun #2",
+            "tenpo mun #3",
+            "tenpo mun #4",
+            "tenpo mun #5",
+            "tenpo mun #6",
+            "tenpo mun #7",
+            "tenpo mun #8",
+            "tenpo mun #9",
+            "tenpo mun #10",
+            "tenpo mun #11",
+            "tenpo mun #12",
+        ],
+        [
+            "tenpo mun #1",
+            "tenpo mun #2",
+            "tenpo mun #3",
+            "tenpo mun #4",
+            "tenpo mun #5",
+            "tenpo mun #6",
+            "tenpo mun #7",
+            "tenpo mun #8",
+            "tenpo mun #9",
+            "tenpo mun #10",
+            "tenpo mun #11",
+            "tenpo mun #12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "suno esun #7",
+            "suno esun #1",
+            "suno esun #2",
+            "suno esun #3",
+            "suno esun #4",
+            "suno esun #5",
+            "suno esun #6",
+        ],
+        [
+            "suno esun #7",
+            "suno esun #1",
+            "suno esun #2",
+            "suno esun #3",
+            "suno esun #4",
+            "suno esun #5",
+            "suno esun #6",
+        ],
+        [
+            "suno esun #7",
+            "suno esun #1",
+            "suno esun #2",
+            "suno esun #3",
+            "suno esun #4",
+            "suno esun #5",
+            "suno esun #6",
+        ],
+        ["7", "1", "2", "3", "4", "5", "6"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["pi open suno", "pi pini suno"],
+        ["AM", "PM"],
+        ["AM", "PM"],
+    ),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `tpi` locale.
+pub static TPI: Locale<'static> = Locale {
+    lang: "tpi",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janueri", "Februeri", "Mas", "Epril", "Me", "Jun", "Julai", "Ogas", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Janueri", "Februeri", "Mas", "Epril", "Me", "Jun", "Julai", "Ogas", "Septemba",
+            "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sande", "Mande", "Tunde", "Trinde", "Fonde", "Fraide", "Sarere",
+        ],
+        [
+            "Sande", "Mande", "Tunde", "Trinde", "Fonde", "Fraide", "Sarere",
+        ],
+        [
+            "Sande", "Mande", "Tunde", "Trinde", "Fonde", "Fraide", "Sarere",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `tr` locale.
+pub static TR: Locale<'static> = Locale {
+    lang: "tr",
+    dates: Locale::new_patterns("d MMMM y EEEE", "d MMMM y", "d MMM y", "d.MM.y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül",
+            "Ekim", "Kasım", "Aralık",
+        ],
+        [
+            "Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara",
+        ],
+        ["O", "Ş", "M", "N", "M", "H", "T", "A", "E", "E", "K", "A"],
+    ),
+    days: Locale::new_days(
+        [
+            "Pazar",
+            "Pazartesi",
+            "Salı",
+            "Çarşamba",
+            "Perşembe",
+            "Cuma",
+            "Cumartesi",
+        ],
+        ["Paz", "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt"],
+        ["Pa", "Pt", "Sa", "Ça", "Pe", "Cu", "Ct"],
+        ["P", "P", "S", "Ç", "P", "C", "C"],
+    ),
+    day_periods: Locale::new_day_periods(["ÖÖ", "ÖS"], ["ÖÖ", "ÖS"], ["öö", "ös"]),
+    eras: Locale::new_eras("MÖ", "MS"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `tr-CY` locale.
+pub static TR_CY: Locale<'static> = Locale {
+    lang: "tr-CY",
+    dates: Locale::new_patterns("d MMMM y EEEE", "d MMMM y", "d MMM y", "d.MM.y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül",
+            "Ekim", "Kasım", "Aralık",
+        ],
+        [
+            "Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara",
+        ],
+        ["O", "Ş", "M", "N", "M", "H", "T", "A", "E", "E", "K", "A"],
+    ),
+    days: Locale::new_days(
+        [
+            "Pazar",
+            "Pazartesi",
+            "Salı",
+            "Çarşamba",
+            "Perşembe",
+            "Cuma",
+            "Cumartesi",
+        ],
+        ["Paz", "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt"],
+        ["Pa", "Pt", "Sa", "Ça", "Pe", "Cu", "Ct"],
+        ["P", "P", "S", "Ç", "P", "C", "C"],
+    ),
+    day_periods: Locale::new_day_periods(["ÖÖ", "ÖS"], ["ÖÖ", "ÖS"], ["öö", "ös"]),
+    eras: Locale::new_eras("MÖ", "MS"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `trv` locale.
+pub static TRV: Locale<'static> = Locale {
+    lang: "trv",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `trw` locale.
+pub static TRW: Locale<'static> = Locale {
+    lang: "trw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ts` locale.
+pub static TS: Locale<'static> = Locale {
+    lang: "ts",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Sunguti",
+            "Nyenyenyani",
+            "Nyenyankulu",
+            "Dzivamisoko",
+            "Mudyaxihi",
+            "Khotavuxika",
+            "Mawuwani",
+            "Mhawuri",
+            "Ndzhati",
+            "Nhlangula",
+            "Hukuri",
+            "N’wendzamhala",
+        ],
+        [
+            "Sun", "Yan", "Kul", "Dzi", "Mud", "Kho", "Maw", "Mha", "Ndz", "Nhl", "Huk", "N’w",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Sonta",
+            "Musumbhunuku",
+            "Ravumbirhi",
+            "Ravunharhu",
+            "Ravumune",
+            "Ravuntlhanu",
+            "Mugqivela",
+        ],
+        ["Son", "Mus", "Bir", "Har", "Ne", "Tlh", "Mug"],
+        ["Son", "Mus", "Bir", "Har", "Ne", "Tlh", "Mug"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `tt` locale.
+pub static TT: Locale<'static> = Locale {
+    lang: "tt",
+    dates: Locale::new_patterns(
+        "d MMMM, y 'ел', EEEE",
+        "d MMMM, y 'ел'",
+        "d MMM, y 'ел'",
+        "dd.MM.y",
+    ),
+    times: Locale::new_patterns("H:mm:ss zzzz", "H:mm:ss z", "H:mm:ss", "H:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "гыйнвар",
+            "февраль",
+            "март",
+            "апрель",
+            "май",
+            "июнь",
+            "июль",
+            "август",
+            "сентябрь",
+            "октябрь",
+            "ноябрь",
+            "декабрь",
+        ],
+        [
+            "гыйн.",
+            "фев.",
+            "мар.",
+            "апр.",
+            "май",
+            "июнь",
+            "июль",
+            "авг.",
+            "сент.",
+            "окт.",
+            "нояб.",
+            "дек.",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "якшәмбе",
+            "дүшәмбе",
+            "сишәмбе",
+            "чәршәмбе",
+            "пәнҗешәмбе",
+            "җомга",
+            "шимбә",
+        ],
+        ["якш.", "дүш.", "сиш.", "чәр.", "пәнҗ.", "җом.", "шим."],
+        ["якш.", "дүш.", "сиш.", "чәр.", "пәнҗ.", "җом.", "шим."],
+        ["Я", "Д", "С", "Ч", "П", "Җ", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("б.э.к.", "милади"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `twq` locale.
+pub static TWQ: Locale<'static> = Locale {
+    lang: "twq",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Žanwiye",
+            "Feewiriye",
+            "Marsi",
+            "Awiril",
+            "Me",
+            "Žuweŋ",
+            "Žuyye",
+            "Ut",
+            "Sektanbur",
+            "Oktoobur",
+            "Noowanbur",
+            "Deesanbur",
+        ],
+        [
+            "Žan", "Fee", "Mar", "Awi", "Me", "Žuw", "Žuy", "Ut", "Sek", "Okt", "Noo", "Dee",
+        ],
+        ["Ž", "F", "M", "A", "M", "Ž", "Ž", "U", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Alhadi",
+            "Atinni",
+            "Atalaata",
+            "Alarba",
+            "Alhamiisa",
+            "Alzuma",
+            "Asibti",
+        ],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["Alh", "Ati", "Ata", "Ala", "Alm", "Alz", "Asi"],
+        ["H", "T", "T", "L", "L", "L", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Subbaahi", "Zaarikay b"],
+        ["Subbaahi", "Zaarikay b"],
+        ["Subbaahi", "Zaarikay b"],
+    ),
+    eras: Locale::new_eras("IJ", "IZ"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `tyv` locale.
+pub static TYV: Locale<'static> = Locale {
+    lang: "tyv",
+    dates: Locale::new_patterns("y'ч' MMMM d, EEEE", "y'ч' MMMM d", "y'ч' MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "Январь",
+            "Февраль",
+            "Март",
+            "Апрель",
+            "Май",
+            "Июнь",
+            "Июль",
+            "Август",
+            "Сентябрь",
+            "Октябрь",
+            "Ноябрь",
+            "Декабрь",
+        ],
+        [
+            "Янв.",
+            "Февр.",
+            "Мар.",
+            "Апр.",
+            "Май",
+            "Июн.",
+            "Июл.",
+            "Авг.",
+            "Сент.",
+            "Окт.",
+            "Нояб.",
+            "Дек.",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "Улуг-хүн",
+            "Понедельник",
+            "Вторник",
+            "Среда",
+            "Четверг",
+            "Пятница",
+            "Суббота",
+        ],
+        ["УХ", "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ"],
+        ["УХ", "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ"],
+        ["У", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("БЭЧ", "БЭ"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `tzm` locale.
+pub static TZM: Locale<'static> = Locale {
+    lang: "tzm",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Yennayer", "Yebrayer", "Mars", "Ibrir", "Mayyu", "Yunyu", "Yulyuz", "Ɣuct",
+            "Cutanbir", "Kṭuber", "Nwanbir", "Dujanbir",
+        ],
+        [
+            "Yen", "Yeb", "Mar", "Ibr", "May", "Yun", "Yul", "Ɣuc", "Cut", "Kṭu", "Nwa", "Duj",
+        ],
+        ["Y", "Y", "M", "I", "M", "Y", "Y", "Ɣ", "C", "K", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Asamas",
+            "Aynas",
+            "Asinas",
+            "Akras",
+            "Akwas",
+            "Asimwas",
+            "Asiḍyas",
+        ],
+        ["Asa", "Ayn", "Asn", "Akr", "Akw", "Asm", "Asḍ"],
+        ["Asa", "Ayn", "Asn", "Akr", "Akw", "Asm", "Asḍ"],
+        ["A", "A", "A", "A", "A", "A", "A"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Zdat azal", "Ḍeffir aza"],
+        ["Zdat azal", "Ḍeffir aza"],
+        ["Zdat azal", "Ḍeffir aza"],
+    ),
+    eras: Locale::new_eras("ZƐ", "ḌƐ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `ug` locale.
+pub static UG: Locale<'static> = Locale {
+    lang: "ug",
+    dates: Locale::new_patterns("y d-MMMM، EEEE", "d-MMMM، y", "d-MMM، y", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}، {0}", "{1}، {0}"),
+    months: Locale::new_months(
+        [
+            "يانۋار",
+            "فېۋرال",
+            "مارت",
+            "ئاپرېل",
+            "ماي",
+            "ئىيۇن",
+            "ئىيۇل",
+            "ئاۋغۇست",
+            "سېنتەبىر",
+            "ئۆكتەبىر",
+            "نويابىر",
+            "دېكابىر",
+        ],
+        [
+            "يانۋار",
+            "فېۋرال",
+            "مارت",
+            "ئاپرېل",
+            "ماي",
+            "ئىيۇن",
+            "ئىيۇل",
+            "ئاۋغۇست",
+            "سېنتەبىر",
+            "ئۆكتەبىر",
+            "نويابىر",
+            "دېكابىر",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "يەكشەنبە",
+            "دۈشەنبە",
+            "سەيشەنبە",
+            "چارشەنبە",
+            "پەيشەنبە",
+            "جۈمە",
+            "شەنبە",
+        ],
+        ["يە", "دۈ", "سە", "چا", "پە", "جۈ", "شە"],
+        ["ي", "د", "س", "چ", "پ", "ج", "ش"],
+        ["ي", "د", "س", "چ", "پ", "ج", "ش"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["چۈشتىن بۇرۇن", "چۈشتىن كېيىن"],
+        ["چ.ب", "چ.ك"],
+        ["ب", "ك"],
+    ),
+    eras: Locale::new_eras("BCE", "مىلادىيە"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `uk` locale.
+pub static UK: Locale<'static> = Locale {
+    lang: "uk",
+    dates: Locale::new_patterns(
+        "EEEE, d MMMM y 'р'.",
+        "d MMMM y 'р'.",
+        "d MMM y 'р'.",
+        "dd.MM.yy",
+    ),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "січня",
+            "лютого",
+            "березня",
+            "квітня",
+            "травня",
+            "червня",
+            "липня",
+            "серпня",
+            "вересня",
+            "жовтня",
+            "листопада",
+            "грудня",
+        ],
+        [
+            "січ.",
+            "лют.",
+            "бер.",
+            "квіт.",
+            "трав.",
+            "черв.",
+            "лип.",
+            "серп.",
+            "вер.",
+            "жовт.",
+            "лист.",
+            "груд.",
+        ],
+        ["с", "л", "б", "к", "т", "ч", "л", "с", "в", "ж", "л", "г"],
+    ),
+    days: Locale::new_days(
+        [
+            "неділя",
+            "понеділок",
+            "вівторок",
+            "середа",
+            "четвер",
+            "пʼятниця",
+            "субота",
+        ],
+        ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["нд", "пн", "вт", "ср", "чт", "пт", "сб"],
+        ["Н", "П", "В", "С", "Ч", "П", "С"],
+    ),
+    day_periods: Locale::new_day_periods(["дп", "пп"], ["дп", "пп"], ["дп", "пп"]),
+    eras: Locale::new_eras("до н. е.", "н. е."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `und` locale.
+pub static UND: Locale<'static> = Locale {
+    lang: "und",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ur` locale.
+pub static UR: Locale<'static> = Locale {
+    lang: "ur",
+    dates: Locale::new_patterns("EEEE، d MMMM، y", "d MMMM، y", "d MMM، y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئی",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئی",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("قبل مسیح", "عیسوی"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ur-IN` locale.
+pub static UR_IN: Locale<'static> = Locale {
+    lang: "ur-IN",
+    dates: Locale::new_patterns("EEEE، d MMMM، y", "d MMMM، y", "d MMM، y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئی",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنوری",
+            "فروری",
+            "مارچ",
+            "اپریل",
+            "مئی",
+            "جون",
+            "جولائی",
+            "اگست",
+            "ستمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["اتوار", "پیر", "منگل", "بدھ", "جمعرات", "جمعہ", "ہفتہ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("قبل مسیح", "عیسوی"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `uz` locale.
+pub static UZ: Locale<'static> = Locale {
+    lang: "uz",
+    dates: Locale::new_patterns("EEEE, d-MMMM, y", "d-MMMM, y", "d-MMM, y", "dd/MM/yy"),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avgust", "sentabr",
+            "oktabr", "noyabr", "dekabr",
+        ],
+        [
+            "yan", "fev", "mar", "apr", "may", "iyn", "iyl", "avg", "sen", "okt", "noy", "dek",
+        ],
+        ["Y", "F", "M", "A", "M", "I", "I", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "yakshanba",
+            "dushanba",
+            "seshanba",
+            "chorshanba",
+            "payshanba",
+            "juma",
+            "shanba",
+        ],
+        ["Yak", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan"],
+        ["Ya", "Du", "Se", "Ch", "Pa", "Ju", "Sh"],
+        ["Y", "D", "S", "C", "P", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["TO", "TK"], ["TO", "TK"], ["TO", "TK"]),
+    eras: Locale::new_eras("m.a.", "milodiy"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `uz-Arab` locale.
+pub static UZ_ARAB: Locale<'static> = Locale {
+    lang: "uz-Arab",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "جنوری",
+            "فبروری",
+            "مارچ",
+            "اپریل",
+            "می",
+            "جون",
+            "جولای",
+            "اگست",
+            "سپتمبر",
+            "اکتوبر",
+            "نومبر",
+            "دسمبر",
+        ],
+        [
+            "جنو", "فبر", "مار", "اپر", "می", "جون", "جول", "اگس", "سپت", "اکت", "نوم", "دسم",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "یکشنبه",
+            "دوشنبه",
+            "سه‌شنبه",
+            "چهارشنبه",
+            "پنجشنبه",
+            "جمعه",
+            "شنبه",
+        ],
+        ["ی.", "د.", "س.", "چ.", "پ.", "ج.", "ش."],
+        ["ی.", "د.", "س.", "چ.", "پ.", "ج.", "ش."],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `uz-Cyrl` locale.
+pub static UZ_CYRL: Locale<'static> = Locale {
+    lang: "uz-Cyrl",
+    dates: Locale::new_patterns("EEEE, dd MMMM, y", "d MMMM, y", "d MMM, y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss (zzzz)", "HH:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "январ",
+            "феврал",
+            "март",
+            "апрел",
+            "май",
+            "июн",
+            "июл",
+            "август",
+            "сентябр",
+            "октябр",
+            "ноябр",
+            "декабр",
+        ],
+        [
+            "янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек",
+        ],
+        ["Я", "Ф", "М", "А", "М", "И", "И", "А", "С", "О", "Н", "Д"],
+    ),
+    days: Locale::new_days(
+        [
+            "якшанба",
+            "душанба",
+            "сешанба",
+            "чоршанба",
+            "пайшанба",
+            "жума",
+            "шанба",
+        ],
+        ["якш", "душ", "сеш", "чор", "пай", "жум", "шан"],
+        ["як", "ду", "се", "чо", "па", "жу", "ша"],
+        ["Я", "Д", "С", "Ч", "П", "Ж", "Ш"],
+    ),
+    day_periods: Locale::new_day_periods(["ТО", "ТК"], ["ТО", "ТК"], ["ТО", "ТК"]),
+    eras: Locale::new_eras("м.а.", "милодий"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `uz-Latn` locale.
+pub static UZ_LATN: Locale<'static> = Locale {
+    lang: "uz-Latn",
+    dates: Locale::new_patterns("EEEE, d-MMMM, y", "d-MMMM, y", "d-MMM, y", "dd/MM/yy"),
+    times: Locale::new_patterns("H:mm:ss (zzzz)", "H:mm:ss (z)", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avgust", "sentabr",
+            "oktabr", "noyabr", "dekabr",
+        ],
+        [
+            "yan", "fev", "mar", "apr", "may", "iyn", "iyl", "avg", "sen", "okt", "noy", "dek",
+        ],
+        ["Y", "F", "M", "A", "M", "I", "I", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "yakshanba",
+            "dushanba",
+            "seshanba",
+            "chorshanba",
+            "payshanba",
+            "juma",
+            "shanba",
+        ],
+        ["Yak", "Dush", "Sesh", "Chor", "Pay", "Jum", "Shan"],
+        ["Ya", "Du", "Se", "Ch", "Pa", "Ju", "Sh"],
+        ["Y", "D", "S", "C", "P", "J", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["TO", "TK"], ["TO", "TK"], ["TO", "TK"]),
+    eras: Locale::new_eras("m.a.", "milodiy"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `vai` locale.
+pub static VAI: Locale<'static> = Locale {
+    lang: "vai",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ꖨꖕ ꕪꕴ ꔞꔀꕮꕊ",
+            "ꕒꕡꖝꖕ",
+            "ꕾꖺ",
+            "ꖢꖕ",
+            "ꖑꕱ",
+            "ꖱꘋ",
+            "ꖱꕞꔤ",
+            "ꗛꔕ",
+            "ꕢꕌ",
+            "ꕭꖃ",
+            "ꔞꘋꕔꕿ ꕸꖃꗏ",
+            "ꖨꖕ ꕪꕴ ꗏꖺꕮꕊ",
+        ],
+        [
+            "ꖨꖕꔞ",
+            "ꕒꕡ",
+            "ꕾꖺ",
+            "ꖢꖕ",
+            "ꖑꕱ",
+            "ꖱꘋ",
+            "ꖱꕞ",
+            "ꗛꔕ",
+            "ꕢꕌ",
+            "ꕭꖃ",
+            "ꔞꘋ",
+            "ꖨꖕꗏ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `vai-Latn` locale.
+pub static VAI_LATN: Locale<'static> = Locale {
+    lang: "vai-Latn",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "lahadi",
+            "tɛɛnɛɛ",
+            "talata",
+            "alaba",
+            "aimisa",
+            "aijima",
+            "siɓiti",
+        ],
+        [
+            "lahadi",
+            "tɛɛnɛɛ",
+            "talata",
+            "alaba",
+            "aimisa",
+            "aijima",
+            "siɓiti",
+        ],
+        [
+            "lahadi",
+            "tɛɛnɛɛ",
+            "talata",
+            "alaba",
+            "aimisa",
+            "aijima",
+            "siɓiti",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `vai-Vaii` locale.
+pub static VAI_VAII: Locale<'static> = Locale {
+    lang: "vai-Vaii",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ꖨꖕ ꕪꕴ ꔞꔀꕮꕊ",
+            "ꕒꕡꖝꖕ",
+            "ꕾꖺ",
+            "ꖢꖕ",
+            "ꖑꕱ",
+            "ꖱꘋ",
+            "ꖱꕞꔤ",
+            "ꗛꔕ",
+            "ꕢꕌ",
+            "ꕭꖃ",
+            "ꔞꘋꕔꕿ ꕸꖃꗏ",
+            "ꖨꖕ ꕪꕴ ꗏꖺꕮꕊ",
+        ],
+        [
+            "ꖨꖕꔞ",
+            "ꕒꕡ",
+            "ꕾꖺ",
+            "ꖢꖕ",
+            "ꖑꕱ",
+            "ꖱꘋ",
+            "ꖱꕞ",
+            "ꗛꔕ",
+            "ꕢꕌ",
+            "ꕭꖃ",
+            "ꔞꘋ",
+            "ꖨꖕꗏ",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["ꕞꕌꔵ", "ꗳꗡꘉ", "ꕚꕞꕚ", "ꕉꕞꕒ", "ꕉꔤꕆꕢ", "ꕉꔤꕀꕮ", "ꔻꔬꔳ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `ve` locale.
+pub static VE: Locale<'static> = Locale {
+    lang: "ve",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `vec` locale.
+pub static VEC: Locale<'static> = Locale {
+    lang: "vec",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "jenaro", "febraro", "marso", "aprile", "majo", "jugno", "lujo", "agosto", "setenbre",
+            "otobre", "novenbre", "dezenbre",
+        ],
+        [
+            "jen", "feb", "mar", "apr", "maj", "jug", "luj", "ago", "set", "oto", "nov", "dez",
+        ],
+        ["J", "F", "M", "A", "M", "J", "L", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "doménega",
+            "luni",
+            "marti",
+            "mèrcore",
+            "zoba",
+            "vènare",
+            "sabo",
+        ],
+        ["dom", "lun", "mar", "mer", "zob", "vèn", "sab"],
+        ["dom", "lun", "mar", "mer", "zob", "vèn", "sab"],
+        ["D", "L", "M", "M", "Z", "V", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v.C.", "d.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `vi` locale.
+pub static VI: Locale<'static> = Locale {
+    lang: "vi",
+    dates: Locale::new_patterns("EEEE, d MMMM, y", "d MMMM, y", "d MMM, y", "d/M/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{0} {1}", "{0} {1}", "{0} {1}", "{0} {1}"),
+    months: Locale::new_months(
+        [
+            "tháng 1",
+            "tháng 2",
+            "tháng 3",
+            "tháng 4",
+            "tháng 5",
+            "tháng 6",
+            "tháng 7",
+            "tháng 8",
+            "tháng 9",
+            "tháng 10",
+            "tháng 11",
+            "tháng 12",
+        ],
+        [
+            "thg 1", "thg 2", "thg 3", "thg 4", "thg 5", "thg 6", "thg 7", "thg 8", "thg 9",
+            "thg 10", "thg 11", "thg 12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Chủ Nhật",
+            "Thứ Hai",
+            "Thứ Ba",
+            "Thứ Tư",
+            "Thứ Năm",
+            "Thứ Sáu",
+            "Thứ Bảy",
+        ],
+        ["CN", "Thứ 2", "Thứ 3", "Thứ 4", "Thứ 5", "Thứ 6", "Thứ 7"],
+        ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
+        ["CN", "T2", "T3", "T4", "T5", "T6", "T7"],
+    ),
+    day_periods: Locale::new_day_periods(["SA", "CH"], ["SA", "CH"], ["s", "c"]),
+    eras: Locale::new_eras("TCN", "SCN"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `vmw` locale.
+pub static VMW: Locale<'static> = Locale {
+    lang: "vmw",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "janeiru",
+            "fevereiru",
+            "marsu",
+            "abril",
+            "maiu",
+            "junyu",
+            "julyu",
+            "agostu",
+            "setembru",
+            "outubru",
+            "novembru",
+            "dezembru",
+        ],
+        [
+            "janeiru",
+            "fevereiru",
+            "marsu",
+            "abril",
+            "maiu",
+            "junyu",
+            "julyu",
+            "agostu",
+            "setembru",
+            "outubru",
+            "novembru",
+            "dezembru",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ettiminku",
+            "nihiku noolempwa",
+            "namaanli",
+            "namararu",
+            "namaxexe",
+            "namathanu",
+            "esaabadu",
+        ],
+        [
+            "ettiminku",
+            "nihiku noolempwa",
+            "namaanli",
+            "namararu",
+            "namaxexe",
+            "namathanu",
+            "esaabadu",
+        ],
+        [
+            "ettiminku",
+            "nihiku noolempwa",
+            "namaanli",
+            "namararu",
+            "namaxexe",
+            "namathanu",
+            "esaabadu",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["vaasisu", "makaaripi"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `vo` locale.
+pub static VO: Locale<'static> = Locale {
+    lang: "vo",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `vun` locale.
+pub static VUN: Locale<'static> = Locale {
+    lang: "vun",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januari", "Februari", "Machi", "Aprilyi", "Mei", "Junyi", "Julyai", "Agusti",
+            "Septemba", "Oktoba", "Novemba", "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mac", "Apr", "Mei", "Jun", "Jul", "Ago", "Sep", "Okt", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Jumapilyi",
+            "Jumatatuu",
+            "Jumanne",
+            "Jumatanu",
+            "Alhamisi",
+            "Ijumaa",
+            "Jumamosi",
+        ],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["Jpi", "Jtt", "Jnn", "Jtn", "Alh", "Iju", "Jmo"],
+        ["J", "J", "J", "J", "A", "I", "J"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+        ["utuko", "kyiukonyi"],
+    ),
+    eras: Locale::new_eras("KK", "BK"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `wa` locale.
+pub static WA: Locale<'static> = Locale {
+    lang: "wa",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `wae` locale.
+pub static WAE: Locale<'static> = Locale {
+    lang: "wae",
+    dates: Locale::new_patterns("EEEE, d. MMMM y", "d. MMMM y", "d. MMM y", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Jenner",
+            "Hornig",
+            "Märze",
+            "Abrille",
+            "Meije",
+            "Bráčet",
+            "Heiwet",
+            "Öigšte",
+            "Herbštmánet",
+            "Wímánet",
+            "Wintermánet",
+            "Chrištmánet",
+        ],
+        [
+            "Jen", "Hor", "Mär", "Abr", "Mei", "Brá", "Hei", "Öig", "Her", "Wím", "Win", "Chr",
+        ],
+        ["J", "H", "M", "A", "M", "B", "H", "Ö", "H", "W", "W", "C"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sunntag", "Mäntag", "Zištag", "Mittwuč", "Fróntag", "Fritag", "Samštag",
+        ],
+        ["Sun", "Män", "Ziš", "Mit", "Fró", "Fri", "Sam"],
+        ["Sun", "Män", "Ziš", "Mit", "Fró", "Fri", "Sam"],
+        ["S", "M", "Z", "M", "F", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("v. Chr.", "n. Chr"),
+    decimal: ',',
+    group: '\'',
+};
+/// Data for `wal` locale.
+pub static WAL: Locale<'static> = Locale {
+    lang: "wal",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `wbp` locale.
+pub static WBP: Locale<'static> = Locale {
+    lang: "wbp",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `wo` locale.
+pub static WO: Locale<'static> = Locale {
+    lang: "wo",
+    dates: Locale::new_patterns("EEEE, d MMM, y", "d MMMM, y", "d MMM, y", "dd-MM-y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} - {0}", "{1} - {0}", "{1} - {0}", "{1} - {0}"),
+    months: Locale::new_months(
+        [
+            "Samwiyee",
+            "Fewriyee",
+            "Mars",
+            "Awril",
+            "Mee",
+            "Suwe",
+            "Sulet",
+            "Ut",
+            "Sàttumbar",
+            "Oktoobar",
+            "Nowàmbar",
+            "Desàmbar",
+        ],
+        [
+            "Sam", "Few", "Mar", "Awr", "Mee", "Suw", "Sul", "Ut", "Sàt", "Okt", "Now", "Des",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "Dibéer", "Altine", "Talaata", "Àlarba", "Alxamis", "Àjjuma", "Aseer",
+        ],
+        ["Dib", "Alt", "Tal", "Àla", "Alx", "Àjj", "Ase"],
+        ["Dib", "Alt", "Tal", "Àla", "Alx", "Àjj", "Ase"],
+        ["Dib", "Alt", "Tal", "Àla", "Alx", "Àjj", "Ase"],
+    ),
+    day_periods: Locale::new_day_periods(["Sub", "Ngo"], ["Sub", "Ngo"], ["Sub", "Ngo"]),
+    eras: Locale::new_eras("JC", "AD"),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `xh` locale.
+pub static XH: Locale<'static> = Locale {
+    lang: "xh",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janyuwari",
+            "Februwari",
+            "Matshi",
+            "Epreli",
+            "Meyi",
+            "Juni",
+            "Julayi",
+            "Agasti",
+            "Septemba",
+            "Okthobha",
+            "Novemba",
+            "Disemba",
+        ],
+        [
+            "Jan", "Feb", "Mat", "Epr", "Mey", "Jun", "Jul", "Aga", "Sept", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Cawe",
+            "Mvulo",
+            "Lwesibini",
+            "Lwesithathu",
+            "Lwesine",
+            "Lwesihlanu",
+            "Mgqibelo",
+        ],
+        ["Caw", "Mvu", "Lwesb", "Tha", "Sin", "Hla", "Mgq"],
+        ["Caw", "Mvu", "Lwesb", "Tha", "Sin", "Hla", "Mgq"],
+        ["C", "Mv", "Sb", "Tht", "Sin", "Hl", "Mg"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ' ',
+};
+/// Data for `xnr` locale.
+pub static XNR: Locale<'static> = Locale {
+    lang: "xnr",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "d/M/yy"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1}, {0}", "{1}, {0}", "{1}, {0}", "{1}, {0}"),
+    months: Locale::new_months(
+        [
+            "जनवरी",
+            "फ़रवरी",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुलाई",
+            "अगस्त",
+            "सितंबर",
+            "अक्तूबर",
+            "नवंबर",
+            "दिसंबर",
+        ],
+        [
+            "जन॰",
+            "फ़र॰",
+            "मार्च",
+            "अप्रैल",
+            "मई",
+            "जून",
+            "जुल॰",
+            "अग॰",
+            "सित॰",
+            "अक्तू॰",
+            "नव॰",
+            "दिस॰",
+        ],
+        [
+            "ज", "फ़", "मा", "अ", "म", "जू", "जु", "अ", "सि", "अ", "न", "दि",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "तोआर",
+            "सोआर",
+            "मंगलवार",
+            "बुधवार",
+            "वीरवार",
+            "शुक्करवार",
+            "शनिच्चरवार",
+        ],
+        ["तोआर", "सोआर", "मंगल", "बुध", "वीर", "शुक्कर", "शनि"],
+        ["त", "सो", "मं", "बु", "वी", "शु", "श"],
+        ["त", "सो", "मं", "बु", "वी", "शु", "श"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["भ्यागा", "दपेहरा/संजा"],
+        ["भ्यागा", "दपेहरा/संजा"],
+        ["भ्यागा", "दपेहरा/संजा"],
+    ),
+    eras: Locale::new_eras("ईसा-पैले", "ईस्वी"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `xog` locale.
+pub static XOG: Locale<'static> = Locale {
+    lang: "xog",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Janwaliyo",
+            "Febwaliyo",
+            "Marisi",
+            "Apuli",
+            "Maayi",
+            "Juuni",
+            "Julaayi",
+            "Agusito",
+            "Sebuttemba",
+            "Okitobba",
+            "Novemba",
+            "Desemba",
+        ],
+        [
+            "Jan", "Feb", "Mar", "Apu", "Maa", "Juu", "Jul", "Agu", "Seb", "Oki", "Nov", "Des",
+        ],
+        ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "Sabiiti",
+            "Balaza",
+            "Owokubili",
+            "Owokusatu",
+            "Olokuna",
+            "Olokutaanu",
+            "Olomukaaga",
+        ],
+        ["Sabi", "Bala", "Kubi", "Kusa", "Kuna", "Kuta", "Muka"],
+        ["Sabi", "Bala", "Kubi", "Kusa", "Kuna", "Kuta", "Muka"],
+        ["S", "B", "B", "S", "K", "K", "M"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["Munkyo", "Eigulo"],
+        ["Munkyo", "Eigulo"],
+        ["Munkyo", "Eigulo"],
+    ),
+    eras: Locale::new_eras("AZ", "AF"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yav` locale.
+pub static YAV: Locale<'static> = Locale {
+    lang: "yav",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "pikítíkítie, oólí ú kutúan",
+            "siɛyɛ́, oóli ú kándíɛ",
+            "ɔnsúmbɔl, oóli ú kátátúɛ",
+            "mesiŋ, oóli ú kénie",
+            "ensil, oóli ú kátánuɛ",
+            "ɔsɔn",
+            "efute",
+            "pisuyú",
+            "imɛŋ i puɔs",
+            "imɛŋ i putúk,oóli ú kátíɛ",
+            "makandikɛ",
+            "pilɔndɔ́",
+        ],
+        [
+            "o.1", "o.2", "o.3", "o.4", "o.5", "o.6", "o.7", "o.8", "o.9", "o.10", "o.11", "o.12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "sɔ́ndiɛ",
+            "móndie",
+            "muányáŋmóndie",
+            "metúkpíápɛ",
+            "kúpélimetúkpiapɛ",
+            "feléte",
+            "séselé",
+        ],
+        ["sd", "md", "mw", "et", "kl", "fl", "ss"],
+        ["sd", "md", "mw", "et", "kl", "fl", "ss"],
+        ["s", "m", "m", "e", "k", "f", "s"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["kiɛmɛ́ɛm", "kisɛ́ndɛ"],
+        ["kiɛmɛ́ɛm", "kisɛ́ndɛ"],
+        ["kiɛmɛ́ɛm", "kisɛ́ndɛ"],
+    ),
+    eras: Locale::new_eras("k.Y.", "+J.C."),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `yi` locale.
+pub static YI: Locale<'static> = Locale {
+    lang: "yi",
+    dates: Locale::new_patterns("EEEE, dטן MMMM y", "dטן MMMM y", "dטן MMM y", "dd/MM/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1}, {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "יאַנואַר",
+            "פֿעברואַר",
+            "מערץ",
+            "אַפּריל",
+            "מיי",
+            "יוני",
+            "יולי",
+            "אויגוסט",
+            "סעפּטעמבער",
+            "אקטאבער",
+            "נאוועמבער",
+            "דעצעמבער",
+        ],
+        [
+            "יאַנואַר",
+            "פֿעברואַר",
+            "מערץ",
+            "אַפּריל",
+            "מיי",
+            "יוני",
+            "יולי",
+            "אויגוסט",
+            "סעפּטעמבער",
+            "אקטאבער",
+            "נאוועמבער",
+            "דעצעמבער",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "זונטיק",
+            "מאָנטיק",
+            "דינסטיק",
+            "מיטוואך",
+            "דאנערשטיק",
+            "פֿרײַטיק",
+            "שבת",
+        ],
+        [
+            "זונטיק",
+            "מאָנטיק",
+            "דינסטיק",
+            "מיטוואך",
+            "דאנערשטיק",
+            "פֿרײַטיק",
+            "שבת",
+        ],
+        [
+            "זונטיק",
+            "מאָנטיק",
+            "דינסטיק",
+            "מיטוואך",
+            "דאנערשטיק",
+            "פֿרײַטיק",
+            "שבת",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["פֿאַרמיטאָג", "נאָכמיטאָג"],
+        ["פֿאַרמיטאָג", "נאָכמיטאָג"],
+        ["פֿאַרמיטאָג", "נאָכמיטאָג"],
+    ),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yo` locale.
+pub static YO: Locale<'static> = Locale {
+    lang: "yo",
+    dates: Locale::new_patterns("EEEE, d MMM y", "d MMM y", "d MM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "H:mm:ss z", "H:m:s", "H:m"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Oṣù Ṣẹ́rẹ́",
+            "Oṣù Èrèlè",
+            "Oṣù Ẹrẹ̀nà",
+            "Oṣù Ìgbé",
+            "Oṣù Ẹ̀bibi",
+            "Oṣù Òkúdu",
+            "Oṣù Agẹmọ",
+            "Oṣù Ògún",
+            "Oṣù Owewe",
+            "Oṣù Ọ̀wàrà",
+            "Oṣù Bélú",
+            "Oṣù Ọ̀pẹ̀",
+        ],
+        [
+            "Oṣù Ṣẹ́rẹ́",
+            "Oṣù Èrèlè",
+            "Oṣù Ẹrẹ̀nà",
+            "Oṣù Ìgbé",
+            "Oṣù Ẹ̀bibi",
+            "Oṣù Òkúdu",
+            "Oṣù Agẹmọ",
+            "Oṣù Ògún",
+            "Oṣù Owewe",
+            "Oṣù Ọ̀wàrà",
+            "Oṣù Bélú",
+            "Oṣù Ọ̀pẹ̀",
+        ],
+        ["S", "È", "Ẹ", "Ì", "Ẹ̀", "Ò", "A", "Ò", "O", "Ọ̀", "B", "Ọ̀"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ọjọ́ Àìkú",
+            "Ọjọ́ Ajé",
+            "Ọjọ́ Ìsẹ́gun",
+            "Ọjọ́rú",
+            "Ọjọ́bọ",
+            "Ọjọ́ Ẹtì",
+            "Ọjọ́ Àbámẹ́ta",
+        ],
+        ["Àìkú", "Ajé", "Ìsẹ́gun", "Ọjọ́rú", "Ọjọ́bọ", "Ẹtì", "Àbámẹ́ta"],
+        ["Àìkú", "Ajé", "Ìsẹ́gun", "Ọjọ́rú", "Ọjọ́bọ", "Ẹtì", "Àbámẹ́ta"],
+        ["À", "A", "Ì", "Ọ", "Ọ", "Ẹ", "À"],
+    ),
+    day_periods: Locale::new_day_periods(["Àárọ̀", "Ọ̀sán"], ["Àárọ̀", "Ọ̀sán"], ["Àárọ̀", "Ọ̀sán"]),
+    eras: Locale::new_eras("BCE", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yo-BJ` locale.
+pub static YO_BJ: Locale<'static> = Locale {
+    lang: "yo-BJ",
+    dates: Locale::new_patterns("EEEE, d MMM y", "d MMM y", "d MM y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "H:mm:ss z", "H:m:s", "H:m"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Oshù Shɛ́rɛ́",
+            "Oshù Èrèlè",
+            "Oshù Ɛrɛ̀nà",
+            "Oshù Ìgbé",
+            "Oshù Ɛ̀bibi",
+            "Oshù Òkúdu",
+            "Oshù Agɛmɔ",
+            "Oshù Ògún",
+            "Oshù Owewe",
+            "Oshù Ɔ̀wàrà",
+            "Oshù Bélú",
+            "Oshù Ɔ̀pɛ̀",
+        ],
+        [
+            "Oshù Shɛ́rɛ́",
+            "Oshù Èrèlè",
+            "Oshù Ɛrɛ̀nà",
+            "Oshù Ìgbé",
+            "Oshù Ɛ̀bibi",
+            "Oshù Òkúdu",
+            "Oshù Agɛmɔ",
+            "Oshù Ògún",
+            "Oshù Owewe",
+            "Oshù Ɔ̀wàrà",
+            "Oshù Bélú",
+            "Oshù Ɔ̀pɛ̀",
+        ],
+        ["S", "È", "Ɛ", "Ì", "Ɛ̀", "Ò", "A", "Ò", "O", "Ɔ̀", "B", "Ɔ̀"],
+    ),
+    days: Locale::new_days(
+        [
+            "Ɔjɔ́ Àìkú",
+            "Ɔjɔ́ Ajé",
+            "Ɔjɔ́ Ìsɛ́gun",
+            "Ɔjɔ́rú",
+            "Ɔjɔ́bɔ",
+            "Ɔjɔ́ Ɛtì",
+            "Ɔjɔ́ Àbámɛ́ta",
+        ],
+        ["Àìkú", "Ajé", "Ìsɛ́gun", "Ɔjɔ́rú", "Ɔjɔ́bɔ", "Ɛtì", "Àbámɛ́ta"],
+        ["Àìkú", "Ajé", "Ìsɛ́gun", "Ɔjɔ́rú", "Ɔjɔ́bɔ", "Ɛtì", "Àbámɛ́ta"],
+        ["À", "A", "Ì", "Ɔ", "Ɔ", "Ɛ", "À"],
+    ),
+    day_periods: Locale::new_day_periods(["Àárɔ̀", "Ɔ̀sán"], ["Àárɔ̀", "Ɔ̀sán"], ["Àárɔ̀", "Ɔ̀sán"]),
+    eras: Locale::new_eras("BCE", "AD"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yrl` locale.
+pub static YRL: Locale<'static> = Locale {
+    lang: "yrl",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "yepé",
+            "mukũi",
+            "musapíri",
+            "irũdí",
+            "pú",
+            "pú-yepé",
+            "pú-mukũi",
+            "pú-musapíri",
+            "pú-irũdí",
+            "yepé-putimaã",
+            "yepé-yepé",
+            "yepé-mukũi",
+        ],
+        [
+            "ye", "mk", "ms", "id", "pu", "py", "pm", "ps", "pi", "yp", "yy", "ym",
+        ],
+        ["Y", "M", "M", "I", "P", "P", "P", "P", "P", "Y", "Y", "Y"],
+    ),
+    days: Locale::new_days(
+        [
+            "mituú",
+            "murakipí",
+            "murakí-mukũi",
+            "murakí-musapíri",
+            "supapá",
+            "yukuakú",
+            "saurú",
+        ],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["M", "M", "M", "M", "S", "Y", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("K.s.", "K.a."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `yrl-CO` locale.
+pub static YRL_CO: Locale<'static> = Locale {
+    lang: "yrl-CO",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "yepé",
+            "mukũi",
+            "musapíri",
+            "irũdí",
+            "pú",
+            "pú-yepé",
+            "pú-mukũi",
+            "pú-musapíri",
+            "pú-irũdí",
+            "yepé-putimaã",
+            "yepé-yepé",
+            "yepé-mukũi",
+        ],
+        [
+            "ye", "mk", "ms", "id", "pu", "py", "pm", "ps", "pi", "yp", "yy", "ym",
+        ],
+        ["Y", "M", "M", "I", "P", "P", "P", "P", "P", "Y", "Y", "Y"],
+    ),
+    days: Locale::new_days(
+        [
+            "mituú",
+            "murakipí",
+            "murakí-mukũi",
+            "murakí-musapíri",
+            "supapá",
+            "yukuakú",
+            "saurú",
+        ],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["M", "M", "M", "M", "S", "Y", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("K.s.", "K.a."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `yrl-VE` locale.
+pub static YRL_VE: Locale<'static> = Locale {
+    lang: "yrl-VE",
+    dates: Locale::new_patterns("EEEE, d MMMM y", "d MMMM y", "d MMM y", "dd/MM/y"),
+    times: Locale::new_patterns("h:mm:ss a zzzz", "h:mm:ss a z", "h:mm:ss a", "h:mm a"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "yepé",
+            "mukũi",
+            "musapíri",
+            "irũdí",
+            "pú",
+            "pú-yepé",
+            "pú-mukũi",
+            "pú-musapíri",
+            "pú-irũdí",
+            "yepé-putimaã",
+            "yepé-yepé",
+            "yepé-mukũi",
+        ],
+        [
+            "ye", "mk", "ms", "id", "pu", "py", "pm", "ps", "pi", "yp", "yy", "ym",
+        ],
+        ["Y", "M", "M", "I", "P", "P", "P", "P", "P", "Y", "Y", "Y"],
+    ),
+    days: Locale::new_days(
+        [
+            "mituú",
+            "murakipí",
+            "murakí-mukũi",
+            "murakí-musapíri",
+            "supapá",
+            "yukuakú",
+            "saurú",
+        ],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["mit", "mur", "mmk", "mms", "sup", "yuk", "sau"],
+        ["M", "M", "M", "M", "S", "Y", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+        ["a. m.", "p. m."],
+    ),
+    eras: Locale::new_eras("K.s.", "K.a."),
+    decimal: ',',
+    group: '.',
+};
+/// Data for `yue` locale.
+pub static YUE: Locale<'static> = Locale {
+    lang: "yue",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("ah:mm:ss [zzzz]", "ah:mm:ss [z]", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上晝", "下晝"], ["上晝", "下晝"], ["上晝", "下晝"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yue-Hans` locale.
+pub static YUE_HANS: Locale<'static> = Locale {
+    lang: "yue-Hans",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("HH:mm:ss [zzzz]", "HH:mm:ss [z]", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上昼", "下昼"], ["上昼", "下昼"], ["上昼", "下昼"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yue-Hant` locale.
+pub static YUE_HANT: Locale<'static> = Locale {
+    lang: "yue-Hant",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("ah:mm:ss [zzzz]", "ah:mm:ss [z]", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上晝", "下晝"], ["上晝", "下晝"], ["上晝", "下晝"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yue-Hant-CN` locale.
+pub static YUE_HANT_CN: Locale<'static> = Locale {
+    lang: "yue-Hant-CN",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("HH:mm:ss [zzzz]", "HH:mm:ss [z]", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上晝", "下晝"], ["上晝", "下晝"], ["上晝", "下晝"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `yue-Hant-MO` locale.
+pub static YUE_HANT_MO: Locale<'static> = Locale {
+    lang: "yue-Hant-MO",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("ah:mm:ss [zzzz]", "ah:mm:ss [z]", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上晝", "下晝"], ["上晝", "下晝"], ["上晝", "下晝"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `za` locale.
+pub static ZA: Locale<'static> = Locale {
+    lang: "za",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ndwenit",
+            "ndwenngeih",
+            "ndwensam",
+            "ndwenseiq",
+            "ndwenngux",
+            "ndwenloeg",
+            "ndwencaet",
+            "ndwenbet",
+            "ndwengouj",
+            "ndwencib",
+            "ndwencib’it",
+            "ndwencibngeih",
+        ],
+        [
+            "ndwenit",
+            "ndwenngeih",
+            "ndwensam",
+            "ndwenseiq",
+            "ndwenngux",
+            "ndwenloeg",
+            "ndwencaet",
+            "ndwenbet",
+            "ndwengouj",
+            "ndwencib",
+            "ndwencib’it",
+            "ndwencibngeih",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "ngoenzsinghgiz",
+            "singhgizit",
+            "singhgizngeih",
+            "singhgizsam",
+            "singhgizseiq",
+            "singhgizhaj",
+            "singhgizroek",
+        ],
+        [
+            "ngoenzsinghgiz",
+            "singhgizit",
+            "singhgizngeih",
+            "singhgizsam",
+            "singhgizseiq",
+            "singhgizhaj",
+            "singhgizroek",
+        ],
+        [
+            "ngoenzsinghgiz",
+            "singhgizit",
+            "singhgizngeih",
+            "singhgizsam",
+            "singhgizseiq",
+            "singhgizhaj",
+            "singhgizroek",
+        ],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["banhaet", "banringzgvaq"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zgh` locale.
+pub static ZGH: Locale<'static> = Locale {
+    lang: "zgh",
+    dates: Locale::new_patterns("EEEE d MMMM y", "d MMMM y", "d MMM, y", "d/M/y"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "ⵉⵏⵏⴰⵢⵔ",
+            "ⴱⵕⴰⵢⵕ",
+            "ⵎⴰⵕⵚ",
+            "ⵉⴱⵔⵉⵔ",
+            "ⵎⴰⵢⵢⵓ",
+            "ⵢⵓⵏⵢⵓ",
+            "ⵢⵓⵍⵢⵓⵣ",
+            "ⵖⵓⵛⵜ",
+            "ⵛⵓⵜⴰⵏⴱⵉⵔ",
+            "ⴽⵜⵓⴱⵔ",
+            "ⵏⵓⵡⴰⵏⴱⵉⵔ",
+            "ⴷⵓⵊⴰⵏⴱⵉⵔ",
+        ],
+        [
+            "ⵉⵏⵏ",
+            "ⴱⵕⴰ",
+            "ⵎⴰⵕ",
+            "ⵉⴱⵔ",
+            "ⵎⴰⵢ",
+            "ⵢⵓⵏ",
+            "ⵢⵓⵍ",
+            "ⵖⵓⵛ",
+            "ⵛⵓⵜ",
+            "ⴽⵜⵓ",
+            "ⵏⵓⵡ",
+            "ⴷⵓⵊ",
+        ],
+        ["ⵉ", "ⴱ", "ⵎ", "ⵉ", "ⵎ", "ⵢ", "ⵢ", "ⵖ", "ⵛ", "ⴽ", "ⵏ", "ⴷ"],
+    ),
+    days: Locale::new_days(
+        [
+            "ⴰⵙⴰⵎⴰⵙ",
+            "ⴰⵢⵏⴰⵙ",
+            "ⴰⵙⵉⵏⴰⵙ",
+            "ⴰⴽⵕⴰⵙ",
+            "ⴰⴽⵡⴰⵙ",
+            "ⴰⵙⵉⵎⵡⴰⵙ",
+            "ⴰⵙⵉⴹⵢⴰⵙ",
+        ],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["ⴰⵙⴰ", "ⴰⵢⵏ", "ⴰⵙⵉ", "ⴰⴽⵕ", "ⴰⴽⵡ", "ⴰⵙⵉⵎ", "ⴰⵙⵉⴹ"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+        ["ⵜⵉⴼⴰⵡⵜ", "ⵜⴰⴷⴳⴳⵯⴰⵜ"],
+    ),
+    eras: Locale::new_eras("ⴷⴰⵄ", "ⴷⴼⵄ"),
+    decimal: ',',
+    group: ' ',
+};
+/// Data for `zh` locale.
+pub static ZH: Locale<'static> = Locale {
+    lang: "zh",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("zzzz HH:mm:ss", "z HH:mm:ss", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hans` locale.
+pub static ZH_HANS: Locale<'static> = Locale {
+    lang: "zh-Hans",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("zzzz HH:mm:ss", "z HH:mm:ss", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hans-HK` locale.
+pub static ZH_HANS_HK: Locale<'static> = Locale {
+    lang: "zh-Hans-HK",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "d/M/yy"),
+    times: Locale::new_patterns("zzzz ah:mm:ss", "z ah:mm:ss", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hans-MO` locale.
+pub static ZH_HANS_MO: Locale<'static> = Locale {
+    lang: "zh-Hans-MO",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "d/M/yy"),
+    times: Locale::new_patterns("zzzz ah:mm:ss", "z ah:mm:ss", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hans-MY` locale.
+pub static ZH_HANS_MY: Locale<'static> = Locale {
+    lang: "zh-Hans-MY",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("zzzz ah:mm:ss", "z ah:mm:ss", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hans-SG` locale.
+pub static ZH_HANS_SG: Locale<'static> = Locale {
+    lang: "zh-Hans-SG",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "dd/MM/yy"),
+    times: Locale::new_patterns("zzzz ah:mm:ss", "z ah:mm:ss", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "一月",
+            "二月",
+            "三月",
+            "四月",
+            "五月",
+            "六月",
+            "七月",
+            "八月",
+            "九月",
+            "十月",
+            "十一月",
+            "十二月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hant` locale.
+pub static ZH_HANT: Locale<'static> = Locale {
+    lang: "zh-Hant",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("Bh:mm:ss [zzzz]", "Bh:mm:ss [z]", "Bh:mm:ss", "Bh:mm"),
+    date_times: Locale::new_patterns("{1}{0}", "{1}{0}", "{1}{0}", "{1}{0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hant-HK` locale.
+pub static ZH_HANT_HK: Locale<'static> = Locale {
+    lang: "zh-Hant-HK",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "d/M/y"),
+    times: Locale::new_patterns("ah:mm:ss [zzzz]", "ah:mm:ss [z]", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1}{0}", "{1}{0}", "{1}{0}", "{1}{0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hant-MO` locale.
+pub static ZH_HANT_MO: Locale<'static> = Locale {
+    lang: "zh-Hant-MO",
+    dates: Locale::new_patterns("y年M月d日EEEE", "y年M月d日", "y年M月d日", "d/M/y"),
+    times: Locale::new_patterns("ah:mm:ss [zzzz]", "ah:mm:ss [z]", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1}{0}", "{1}{0}", "{1}{0}", "{1}{0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("公元前", "公元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Hant-MY` locale.
+pub static ZH_HANT_MY: Locale<'static> = Locale {
+    lang: "zh-Hant-MY",
+    dates: Locale::new_patterns("y年M月d日 EEEE", "y年M月d日", "y年M月d日", "y/M/d"),
+    times: Locale::new_patterns("zzzz ah:mm:ss", "z ah:mm:ss", "ah:mm:ss", "ah:mm"),
+    date_times: Locale::new_patterns("{1}{0}", "{1}{0}", "{1}{0}", "{1}{0}"),
+    months: Locale::new_months(
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        [
+            "星期日",
+            "星期一",
+            "星期二",
+            "星期三",
+            "星期四",
+            "星期五",
+            "星期六",
+        ],
+        ["週日", "週一", "週二", "週三", "週四", "週五", "週六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+        ["日", "一", "二", "三", "四", "五", "六"],
+    ),
+    day_periods: Locale::new_day_periods(["上午", "下午"], ["上午", "下午"], ["上午", "下午"]),
+    eras: Locale::new_eras("西元前", "西元"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zh-Latn` locale.
+pub static ZH_LATN: Locale<'static> = Locale {
+    lang: "zh-Latn",
+    dates: Locale::new_patterns("y MMMM d, EEEE", "y MMMM d", "y MMM d", "y-MM-dd"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "M01", "M02", "M03", "M04", "M05", "M06", "M07", "M08", "M09", "M10", "M11", "M12",
+        ],
+        [
+            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+        ],
+    ),
+    days: Locale::new_days(
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+        ["S", "M", "T", "W", "T", "F", "S"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["AM", "PM"]),
+    eras: Locale::new_eras("BCE", "CE"),
+    decimal: '.',
+    group: ',',
+};
+/// Data for `zu` locale.
+pub static ZU: Locale<'static> = Locale {
+    lang: "zu",
+    dates: Locale::new_patterns("EEEE, MMMM d, y", "MMMM d, y", "MMM d, y", "M/d/yy"),
+    times: Locale::new_patterns("HH:mm:ss zzzz", "HH:mm:ss z", "HH:mm:ss", "HH:mm"),
+    date_times: Locale::new_patterns("{1} {0}", "{1} {0}", "{1} {0}", "{1} {0}"),
+    months: Locale::new_months(
+        [
+            "Januwari",
+            "Februwari",
+            "Mashi",
+            "Ephreli",
+            "Meyi",
+            "Juni",
+            "Julayi",
+            "Agasti",
+            "Septhemba",
+            "Okthoba",
+            "Novemba",
+            "Disemba",
+        ],
+        [
+            "Jan", "Feb", "Mas", "Eph", "Mey", "Jun", "Jul", "Aga", "Sep", "Okt", "Nov", "Dis",
+        ],
+        ["J", "F", "M", "E", "M", "J", "J", "A", "S", "O", "N", "D"],
+    ),
+    days: Locale::new_days(
+        [
+            "ISonto",
+            "UMsombuluko",
+            "ULwesibili",
+            "ULwesithathu",
+            "ULwesine",
+            "ULwesihlanu",
+            "UMgqibelo",
+        ],
+        ["Son", "Mso", "Bil", "Tha", "Sin", "Hla", "Mgq"],
+        ["Son", "Mso", "Bil", "Tha", "Sin", "Hla", "Mgq"],
+        ["S", "M", "B", "T", "S", "H", "M"],
+    ),
+    day_periods: Locale::new_day_periods(["AM", "PM"], ["AM", "PM"], ["a", "p"]),
+    eras: Locale::new_eras("BC", "AD"),
+    decimal: '.',
+    group: ',',
+};
+
+/// Map from language to locale date patterns.
+pub static LANG_TO_LOCALE: LazyLock<HashMap<&'static str, &'static Locale<'static>>> =
+    LazyLock::new(|| {
+        [
+            ("aa", &AA),
+            ("aa-DJ", &AA_DJ),
+            ("aa-ER", &AA_ER),
+            ("ab", &AB),
+            ("af", &AF),
+            ("af-NA", &AF_NA),
+            ("agq", &AGQ),
+            ("ak", &AK),
+            ("am", &AM),
+            ("an", &AN),
+            ("ann", &ANN),
+            ("apc", &APC),
+            ("ar", &AR),
+            ("ar-AE", &AR_AE),
+            ("ar-BH", &AR_BH),
+            ("ar-DJ", &AR_DJ),
+            ("ar-DZ", &AR_DZ),
+            ("ar-EG", &AR_EG),
+            ("ar-EH", &AR_EH),
+            ("ar-ER", &AR_ER),
+            ("ar-IL", &AR_IL),
+            ("ar-IQ", &AR_IQ),
+            ("ar-JO", &AR_JO),
+            ("ar-KM", &AR_KM),
+            ("ar-KW", &AR_KW),
+            ("ar-LB", &AR_LB),
+            ("ar-LY", &AR_LY),
+            ("ar-MA", &AR_MA),
+            ("ar-MR", &AR_MR),
+            ("ar-OM", &AR_OM),
+            ("ar-PS", &AR_PS),
+            ("ar-QA", &AR_QA),
+            ("ar-SA", &AR_SA),
+            ("ar-SD", &AR_SD),
+            ("ar-SO", &AR_SO),
+            ("ar-SS", &AR_SS),
+            ("ar-SY", &AR_SY),
+            ("ar-TD", &AR_TD),
+            ("ar-TN", &AR_TN),
+            ("ar-YE", &AR_YE),
+            ("arn", &ARN),
+            ("as", &AS),
+            ("asa", &ASA),
+            ("ast", &AST),
+            ("az", &AZ),
+            ("az-Arab", &AZ_ARAB),
+            ("az-Arab-IQ", &AZ_ARAB_IQ),
+            ("az-Arab-TR", &AZ_ARAB_TR),
+            ("az-Cyrl", &AZ_CYRL),
+            ("az-Latn", &AZ_LATN),
+            ("ba", &BA),
+            ("bal", &BAL),
+            ("bal-Arab", &BAL_ARAB),
+            ("bal-Latn", &BAL_LATN),
+            ("bas", &BAS),
+            ("be", &BE),
+            ("be-tarask", &BE_TARASK),
+            ("bem", &BEM),
+            ("bew", &BEW),
+            ("bez", &BEZ),
+            ("bg", &BG),
+            ("bgc", &BGC),
+            ("bgn", &BGN),
+            ("bgn-AE", &BGN_AE),
+            ("bgn-AF", &BGN_AF),
+            ("bgn-IR", &BGN_IR),
+            ("bgn-OM", &BGN_OM),
+            ("bho", &BHO),
+            ("blo", &BLO),
+            ("blt", &BLT),
+            ("bm", &BM),
+            ("bm-Nkoo", &BM_NKOO),
+            ("bn", &BN),
+            ("bn-IN", &BN_IN),
+            ("bo", &BO),
+            ("bo-IN", &BO_IN),
+            ("bqi", &BQI),
+            ("br", &BR),
+            ("brx", &BRX),
+            ("bs", &BS),
+            ("bs-Cyrl", &BS_CYRL),
+            ("bs-Latn", &BS_LATN),
+            ("bss", &BSS),
+            ("bua", &BUA),
+            ("byn", &BYN),
+            ("ca", &CA),
+            ("ca-AD", &CA_AD),
+            ("ca-ES-valencia", &CA_ES_VALENCIA),
+            ("ca-FR", &CA_FR),
+            ("ca-IT", &CA_IT),
+            ("cad", &CAD),
+            ("cch", &CCH),
+            ("ccp", &CCP),
+            ("ccp-IN", &CCP_IN),
+            ("ce", &CE),
+            ("ceb", &CEB),
+            ("cgg", &CGG),
+            ("cho", &CHO),
+            ("chr", &CHR),
+            ("cic", &CIC),
+            ("ckb", &CKB),
+            ("ckb-IR", &CKB_IR),
+            ("co", &CO),
+            ("cop", &COP),
+            ("cs", &CS),
+            ("csw", &CSW),
+            ("cu", &CU),
+            ("cv", &CV),
+            ("cy", &CY),
+            ("da", &DA),
+            ("da-GL", &DA_GL),
+            ("dav", &DAV),
+            ("de", &DE),
+            ("de-AT", &DE_AT),
+            ("de-BE", &DE_BE),
+            ("de-CH", &DE_CH),
+            ("de-IT", &DE_IT),
+            ("de-LI", &DE_LI),
+            ("de-LU", &DE_LU),
+            ("dje", &DJE),
+            ("doi", &DOI),
+            ("dsb", &DSB),
+            ("dua", &DUA),
+            ("dv", &DV),
+            ("dyo", &DYO),
+            ("dz", &DZ),
+            ("ebu", &EBU),
+            ("ee", &EE),
+            ("ee-TG", &EE_TG),
+            ("el", &EL),
+            ("el-CY", &EL_CY),
+            ("el-polyton", &EL_POLYTON),
+            ("en", &EN),
+            ("en-001", &EN_001),
+            ("en-150", &EN_150),
+            ("en-AE", &EN_AE),
+            ("en-AG", &EN_AG),
+            ("en-AI", &EN_AI),
+            ("en-AS", &EN_AS),
+            ("en-AT", &EN_AT),
+            ("en-AU", &EN_AU),
+            ("en-BB", &EN_BB),
+            ("en-BE", &EN_BE),
+            ("en-BI", &EN_BI),
+            ("en-BM", &EN_BM),
+            ("en-BS", &EN_BS),
+            ("en-BW", &EN_BW),
+            ("en-BZ", &EN_BZ),
+            ("en-CA", &EN_CA),
+            ("en-CC", &EN_CC),
+            ("en-CH", &EN_CH),
+            ("en-CK", &EN_CK),
+            ("en-CM", &EN_CM),
+            ("en-CX", &EN_CX),
+            ("en-CY", &EN_CY),
+            ("en-CZ", &EN_CZ),
+            ("en-DE", &EN_DE),
+            ("en-DG", &EN_DG),
+            ("en-DK", &EN_DK),
+            ("en-DM", &EN_DM),
+            ("en-Dsrt", &EN_DSRT),
+            ("en-EE", &EN_EE),
+            ("en-ER", &EN_ER),
+            ("en-ES", &EN_ES),
+            ("en-FI", &EN_FI),
+            ("en-FJ", &EN_FJ),
+            ("en-FK", &EN_FK),
+            ("en-FM", &EN_FM),
+            ("en-FR", &EN_FR),
+            ("en-GB", &EN_GB),
+            ("en-GD", &EN_GD),
+            ("en-GE", &EN_GE),
+            ("en-GG", &EN_GG),
+            ("en-GH", &EN_GH),
+            ("en-GI", &EN_GI),
+            ("en-GM", &EN_GM),
+            ("en-GS", &EN_GS),
+            ("en-GU", &EN_GU),
+            ("en-GY", &EN_GY),
+            ("en-HK", &EN_HK),
+            ("en-HU", &EN_HU),
+            ("en-ID", &EN_ID),
+            ("en-IE", &EN_IE),
+            ("en-IL", &EN_IL),
+            ("en-IM", &EN_IM),
+            ("en-IN", &EN_IN),
+            ("en-IO", &EN_IO),
+            ("en-IT", &EN_IT),
+            ("en-JE", &EN_JE),
+            ("en-JM", &EN_JM),
+            ("en-JP", &EN_JP),
+            ("en-KE", &EN_KE),
+            ("en-KI", &EN_KI),
+            ("en-KN", &EN_KN),
+            ("en-KY", &EN_KY),
+            ("en-LC", &EN_LC),
+            ("en-LR", &EN_LR),
+            ("en-LS", &EN_LS),
+            ("en-LT", &EN_LT),
+            ("en-LV", &EN_LV),
+            ("en-MG", &EN_MG),
+            ("en-MH", &EN_MH),
+            ("en-MO", &EN_MO),
+            ("en-MP", &EN_MP),
+            ("en-MS", &EN_MS),
+            ("en-MT", &EN_MT),
+            ("en-MU", &EN_MU),
+            ("en-MV", &EN_MV),
+            ("en-MW", &EN_MW),
+            ("en-MY", &EN_MY),
+            ("en-NA", &EN_NA),
+            ("en-NF", &EN_NF),
+            ("en-NG", &EN_NG),
+            ("en-NL", &EN_NL),
+            ("en-NO", &EN_NO),
+            ("en-NR", &EN_NR),
+            ("en-NU", &EN_NU),
+            ("en-NZ", &EN_NZ),
+            ("en-PG", &EN_PG),
+            ("en-PH", &EN_PH),
+            ("en-PK", &EN_PK),
+            ("en-PL", &EN_PL),
+            ("en-PN", &EN_PN),
+            ("en-PR", &EN_PR),
+            ("en-PT", &EN_PT),
+            ("en-PW", &EN_PW),
+            ("en-RO", &EN_RO),
+            ("en-RW", &EN_RW),
+            ("en-SB", &EN_SB),
+            ("en-SC", &EN_SC),
+            ("en-SD", &EN_SD),
+            ("en-SE", &EN_SE),
+            ("en-SG", &EN_SG),
+            ("en-SH", &EN_SH),
+            ("en-SI", &EN_SI),
+            ("en-SK", &EN_SK),
+            ("en-SL", &EN_SL),
+            ("en-SS", &EN_SS),
+            ("en-SX", &EN_SX),
+            ("en-SZ", &EN_SZ),
+            ("en-Shaw", &EN_SHAW),
+            ("en-TC", &EN_TC),
+            ("en-TK", &EN_TK),
+            ("en-TO", &EN_TO),
+            ("en-TT", &EN_TT),
+            ("en-TV", &EN_TV),
+            ("en-TZ", &EN_TZ),
+            ("en-UA", &EN_UA),
+            ("en-UG", &EN_UG),
+            ("en-UM", &EN_UM),
+            ("en-VC", &EN_VC),
+            ("en-VG", &EN_VG),
+            ("en-VI", &EN_VI),
+            ("en-VU", &EN_VU),
+            ("en-WS", &EN_WS),
+            ("en-ZA", &EN_ZA),
+            ("en-ZM", &EN_ZM),
+            ("en-ZW", &EN_ZW),
+            ("eo", &EO),
+            ("es", &ES),
+            ("es-419", &ES_419),
+            ("es-AR", &ES_AR),
+            ("es-BO", &ES_BO),
+            ("es-BR", &ES_BR),
+            ("es-BZ", &ES_BZ),
+            ("es-CL", &ES_CL),
+            ("es-CO", &ES_CO),
+            ("es-CR", &ES_CR),
+            ("es-CU", &ES_CU),
+            ("es-DO", &ES_DO),
+            ("es-EA", &ES_EA),
+            ("es-EC", &ES_EC),
+            ("es-GQ", &ES_GQ),
+            ("es-GT", &ES_GT),
+            ("es-HN", &ES_HN),
+            ("es-IC", &ES_IC),
+            ("es-MX", &ES_MX),
+            ("es-NI", &ES_NI),
+            ("es-PA", &ES_PA),
+            ("es-PE", &ES_PE),
+            ("es-PH", &ES_PH),
+            ("es-PR", &ES_PR),
+            ("es-PY", &ES_PY),
+            ("es-SV", &ES_SV),
+            ("es-US", &ES_US),
+            ("es-UY", &ES_UY),
+            ("es-VE", &ES_VE),
+            ("et", &ET),
+            ("eu", &EU),
+            ("ewo", &EWO),
+            ("fa", &FA),
+            ("fa-AF", &FA_AF),
+            ("ff", &FF),
+            ("ff-Adlm", &FF_ADLM),
+            ("ff-Adlm-BF", &FF_ADLM_BF),
+            ("ff-Adlm-CM", &FF_ADLM_CM),
+            ("ff-Adlm-GH", &FF_ADLM_GH),
+            ("ff-Adlm-GM", &FF_ADLM_GM),
+            ("ff-Adlm-GW", &FF_ADLM_GW),
+            ("ff-Adlm-LR", &FF_ADLM_LR),
+            ("ff-Adlm-MR", &FF_ADLM_MR),
+            ("ff-Adlm-NE", &FF_ADLM_NE),
+            ("ff-Adlm-NG", &FF_ADLM_NG),
+            ("ff-Adlm-SL", &FF_ADLM_SL),
+            ("ff-Adlm-SN", &FF_ADLM_SN),
+            ("ff-Latn", &FF_LATN),
+            ("ff-Latn-BF", &FF_LATN_BF),
+            ("ff-Latn-CM", &FF_LATN_CM),
+            ("ff-Latn-GH", &FF_LATN_GH),
+            ("ff-Latn-GM", &FF_LATN_GM),
+            ("ff-Latn-GN", &FF_LATN_GN),
+            ("ff-Latn-GW", &FF_LATN_GW),
+            ("ff-Latn-LR", &FF_LATN_LR),
+            ("ff-Latn-MR", &FF_LATN_MR),
+            ("ff-Latn-NE", &FF_LATN_NE),
+            ("ff-Latn-NG", &FF_LATN_NG),
+            ("ff-Latn-SL", &FF_LATN_SL),
+            ("fi", &FI),
+            ("fil", &FIL),
+            ("fo", &FO),
+            ("fo-DK", &FO_DK),
+            ("fr", &FR),
+            ("fr-BE", &FR_BE),
+            ("fr-BF", &FR_BF),
+            ("fr-BI", &FR_BI),
+            ("fr-BJ", &FR_BJ),
+            ("fr-BL", &FR_BL),
+            ("fr-CA", &FR_CA),
+            ("fr-CD", &FR_CD),
+            ("fr-CF", &FR_CF),
+            ("fr-CG", &FR_CG),
+            ("fr-CH", &FR_CH),
+            ("fr-CI", &FR_CI),
+            ("fr-CM", &FR_CM),
+            ("fr-DJ", &FR_DJ),
+            ("fr-DZ", &FR_DZ),
+            ("fr-GA", &FR_GA),
+            ("fr-GF", &FR_GF),
+            ("fr-GN", &FR_GN),
+            ("fr-GP", &FR_GP),
+            ("fr-GQ", &FR_GQ),
+            ("fr-HT", &FR_HT),
+            ("fr-KM", &FR_KM),
+            ("fr-LU", &FR_LU),
+            ("fr-MA", &FR_MA),
+            ("fr-MC", &FR_MC),
+            ("fr-MF", &FR_MF),
+            ("fr-MG", &FR_MG),
+            ("fr-ML", &FR_ML),
+            ("fr-MQ", &FR_MQ),
+            ("fr-MR", &FR_MR),
+            ("fr-MU", &FR_MU),
+            ("fr-NC", &FR_NC),
+            ("fr-NE", &FR_NE),
+            ("fr-PF", &FR_PF),
+            ("fr-PM", &FR_PM),
+            ("fr-RE", &FR_RE),
+            ("fr-RW", &FR_RW),
+            ("fr-SC", &FR_SC),
+            ("fr-SN", &FR_SN),
+            ("fr-SY", &FR_SY),
+            ("fr-TD", &FR_TD),
+            ("fr-TG", &FR_TG),
+            ("fr-TN", &FR_TN),
+            ("fr-VU", &FR_VU),
+            ("fr-WF", &FR_WF),
+            ("fr-YT", &FR_YT),
+            ("frr", &FRR),
+            ("fur", &FUR),
+            ("fy", &FY),
+            ("ga", &GA),
+            ("ga-GB", &GA_GB),
+            ("gaa", &GAA),
+            ("gd", &GD),
+            ("gez", &GEZ),
+            ("gez-ER", &GEZ_ER),
+            ("gl", &GL),
+            ("gn", &GN),
+            ("gsw", &GSW),
+            ("gsw-FR", &GSW_FR),
+            ("gsw-LI", &GSW_LI),
+            ("gu", &GU),
+            ("guz", &GUZ),
+            ("gv", &GV),
+            ("ha", &HA),
+            ("ha-Arab", &HA_ARAB),
+            ("ha-Arab-SD", &HA_ARAB_SD),
+            ("ha-GH", &HA_GH),
+            ("ha-NE", &HA_NE),
+            ("haw", &HAW),
+            ("he", &HE),
+            ("hi", &HI),
+            ("hi-Latn", &HI_LATN),
+            ("hnj", &HNJ),
+            ("hnj-Hmnp", &HNJ_HMNP),
+            ("hr", &HR),
+            ("hr-BA", &HR_BA),
+            ("hsb", &HSB),
+            ("ht", &HT),
+            ("hu", &HU),
+            ("hy", &HY),
+            ("ia", &IA),
+            ("id", &ID),
+            ("ie", &IE),
+            ("ig", &IG),
+            ("ii", &II),
+            ("io", &IO),
+            ("is", &IS),
+            ("it", &IT),
+            ("it-CH", &IT_CH),
+            ("it-SM", &IT_SM),
+            ("it-VA", &IT_VA),
+            ("iu", &IU),
+            ("iu-Latn", &IU_LATN),
+            ("ja", &JA),
+            ("jbo", &JBO),
+            ("jgo", &JGO),
+            ("jmc", &JMC),
+            ("jv", &JV),
+            ("ka", &KA),
+            ("kaa", &KAA),
+            ("kaa-Cyrl", &KAA_CYRL),
+            ("kaa-Latn", &KAA_LATN),
+            ("kab", &KAB),
+            ("kaj", &KAJ),
+            ("kam", &KAM),
+            ("kcg", &KCG),
+            ("kde", &KDE),
+            ("kea", &KEA),
+            ("kek", &KEK),
+            ("ken", &KEN),
+            ("kgp", &KGP),
+            ("khq", &KHQ),
+            ("ki", &KI),
+            ("kk", &KK),
+            ("kk-Arab", &KK_ARAB),
+            ("kk-Cyrl", &KK_CYRL),
+            ("kk-KZ", &KK_KZ),
+            ("kkj", &KKJ),
+            ("kl", &KL),
+            ("kln", &KLN),
+            ("km", &KM),
+            ("kn", &KN),
+            ("ko", &KO),
+            ("ko-CN", &KO_CN),
+            ("ko-KP", &KO_KP),
+            ("kok", &KOK),
+            ("kok-Deva", &KOK_DEVA),
+            ("kok-Latn", &KOK_LATN),
+            ("kpe", &KPE),
+            ("kpe-GN", &KPE_GN),
+            ("ks", &KS),
+            ("ks-Arab", &KS_ARAB),
+            ("ks-Deva", &KS_DEVA),
+            ("ksb", &KSB),
+            ("ksf", &KSF),
+            ("ksh", &KSH),
+            ("ku", &KU),
+            ("ku-Arab", &KU_ARAB),
+            ("ku-Arab-IR", &KU_ARAB_IR),
+            ("ku-Latn", &KU_LATN),
+            ("ku-Latn-IQ", &KU_LATN_IQ),
+            ("ku-Latn-SY", &KU_LATN_SY),
+            ("ku-TR", &KU_TR),
+            ("kw", &KW),
+            ("kxv", &KXV),
+            ("kxv-Deva", &KXV_DEVA),
+            ("kxv-Latn", &KXV_LATN),
+            ("kxv-Orya", &KXV_ORYA),
+            ("kxv-Telu", &KXV_TELU),
+            ("ky", &KY),
+            ("la", &LA),
+            ("lag", &LAG),
+            ("lb", &LB),
+            ("lg", &LG),
+            ("lij", &LIJ),
+            ("lkt", &LKT),
+            ("lld", &LLD),
+            ("lmo", &LMO),
+            ("ln", &LN),
+            ("ln-AO", &LN_AO),
+            ("ln-CF", &LN_CF),
+            ("ln-CG", &LN_CG),
+            ("lo", &LO),
+            ("lrc", &LRC),
+            ("lrc-IQ", &LRC_IQ),
+            ("lt", &LT),
+            ("ltg", &LTG),
+            ("lu", &LU),
+            ("luo", &LUO),
+            ("luy", &LUY),
+            ("lv", &LV),
+            ("lzz", &LZZ),
+            ("mai", &MAI),
+            ("mas", &MAS),
+            ("mas-TZ", &MAS_TZ),
+            ("mdf", &MDF),
+            ("mer", &MER),
+            ("mfe", &MFE),
+            ("mg", &MG),
+            ("mgh", &MGH),
+            ("mgo", &MGO),
+            ("mhn", &MHN),
+            ("mi", &MI),
+            ("mic", &MIC),
+            ("mk", &MK),
+            ("ml", &ML),
+            ("mn", &MN),
+            ("mn-Mong", &MN_MONG),
+            ("mn-Mong-MN", &MN_MONG_MN),
+            ("mni", &MNI),
+            ("mni-Beng", &MNI_BENG),
+            ("mni-Mtei", &MNI_MTEI),
+            ("moh", &MOH),
+            ("mr", &MR),
+            ("ms", &MS),
+            ("ms-Arab", &MS_ARAB),
+            ("ms-Arab-BN", &MS_ARAB_BN),
+            ("ms-BN", &MS_BN),
+            ("ms-ID", &MS_ID),
+            ("ms-SG", &MS_SG),
+            ("mt", &MT),
+            ("mua", &MUA),
+            ("mus", &MUS),
+            ("mww", &MWW),
+            ("mww-Hmnp", &MWW_HMNP),
+            ("my", &MY),
+            ("myv", &MYV),
+            ("mzn", &MZN),
+            ("naq", &NAQ),
+            ("nb", &NB),
+            ("nb-SJ", &NB_SJ),
+            ("nd", &ND),
+            ("nds", &NDS),
+            ("nds-NL", &NDS_NL),
+            ("ne", &NE),
+            ("ne-IN", &NE_IN),
+            ("nl", &NL),
+            ("nl-AW", &NL_AW),
+            ("nl-BE", &NL_BE),
+            ("nl-BQ", &NL_BQ),
+            ("nl-CW", &NL_CW),
+            ("nl-SR", &NL_SR),
+            ("nl-SX", &NL_SX),
+            ("nmg", &NMG),
+            ("nn", &NN),
+            ("nnh", &NNH),
+            ("no", &NO),
+            ("nqo", &NQO),
+            ("nr", &NR),
+            ("nso", &NSO),
+            ("nus", &NUS),
+            ("nv", &NV),
+            ("ny", &NY),
+            ("nyn", &NYN),
+            ("oc", &OC),
+            ("oc-ES", &OC_ES),
+            ("oka", &OKA),
+            ("oka-US", &OKA_US),
+            ("om", &OM),
+            ("om-KE", &OM_KE),
+            ("or", &OR),
+            ("os", &OS),
+            ("os-RU", &OS_RU),
+            ("osa", &OSA),
+            ("pa", &PA),
+            ("pa-Arab", &PA_ARAB),
+            ("pa-Guru", &PA_GURU),
+            ("pap", &PAP),
+            ("pap-AW", &PAP_AW),
+            ("pcm", &PCM),
+            ("pi", &PI),
+            ("pi-Latn", &PI_LATN),
+            ("pis", &PIS),
+            ("pl", &PL),
+            ("pms", &PMS),
+            ("prg", &PRG),
+            ("ps", &PS),
+            ("ps-PK", &PS_PK),
+            ("pt", &PT),
+            ("pt-AO", &PT_AO),
+            ("pt-CH", &PT_CH),
+            ("pt-CV", &PT_CV),
+            ("pt-GQ", &PT_GQ),
+            ("pt-GW", &PT_GW),
+            ("pt-LU", &PT_LU),
+            ("pt-MO", &PT_MO),
+            ("pt-MZ", &PT_MZ),
+            ("pt-PT", &PT_PT),
+            ("pt-ST", &PT_ST),
+            ("pt-TL", &PT_TL),
+            ("qu", &QU),
+            ("qu-BO", &QU_BO),
+            ("qu-EC", &QU_EC),
+            ("quc", &QUC),
+            ("raj", &RAJ),
+            ("rhg", &RHG),
+            ("rhg-Rohg", &RHG_ROHG),
+            ("rhg-Rohg-BD", &RHG_ROHG_BD),
+            ("rif", &RIF),
+            ("rm", &RM),
+            ("rn", &RN),
+            ("ro", &RO),
+            ("ro-MD", &RO_MD),
+            ("rof", &ROF),
+            ("ru", &RU),
+            ("ru-BY", &RU_BY),
+            ("ru-KG", &RU_KG),
+            ("ru-KZ", &RU_KZ),
+            ("ru-MD", &RU_MD),
+            ("ru-UA", &RU_UA),
+            ("rw", &RW),
+            ("rwk", &RWK),
+            ("sa", &SA),
+            ("sah", &SAH),
+            ("saq", &SAQ),
+            ("sat", &SAT),
+            ("sat-Deva", &SAT_DEVA),
+            ("sat-Olck", &SAT_OLCK),
+            ("sbp", &SBP),
+            ("sc", &SC),
+            ("scn", &SCN),
+            ("sd", &SD),
+            ("sd-Arab", &SD_ARAB),
+            ("sd-Deva", &SD_DEVA),
+            ("sdh", &SDH),
+            ("sdh-IQ", &SDH_IQ),
+            ("se", &SE),
+            ("se-FI", &SE_FI),
+            ("se-SE", &SE_SE),
+            ("seh", &SEH),
+            ("ses", &SES),
+            ("sg", &SG),
+            ("sgs", &SGS),
+            ("shi", &SHI),
+            ("shi-Latn", &SHI_LATN),
+            ("shi-Tfng", &SHI_TFNG),
+            ("shn", &SHN),
+            ("shn-TH", &SHN_TH),
+            ("si", &SI),
+            ("sid", &SID),
+            ("sk", &SK),
+            ("skr", &SKR),
+            ("sl", &SL),
+            ("sma", &SMA),
+            ("sma-NO", &SMA_NO),
+            ("smj", &SMJ),
+            ("smj-NO", &SMJ_NO),
+            ("smn", &SMN),
+            ("sms", &SMS),
+            ("sn", &SN),
+            ("so", &SO),
+            ("so-DJ", &SO_DJ),
+            ("so-ET", &SO_ET),
+            ("so-KE", &SO_KE),
+            ("sq", &SQ),
+            ("sq-MK", &SQ_MK),
+            ("sq-XK", &SQ_XK),
+            ("sr", &SR),
+            ("sr-Cyrl", &SR_CYRL),
+            ("sr-Cyrl-BA", &SR_CYRL_BA),
+            ("sr-Cyrl-ME", &SR_CYRL_ME),
+            ("sr-Cyrl-XK", &SR_CYRL_XK),
+            ("sr-Latn", &SR_LATN),
+            ("sr-Latn-BA", &SR_LATN_BA),
+            ("sr-Latn-ME", &SR_LATN_ME),
+            ("sr-Latn-XK", &SR_LATN_XK),
+            ("ss", &SS),
+            ("ss-SZ", &SS_SZ),
+            ("ssy", &SSY),
+            ("st", &ST),
+            ("st-LS", &ST_LS),
+            ("su", &SU),
+            ("su-Latn", &SU_LATN),
+            ("suz", &SUZ),
+            ("suz-Deva", &SUZ_DEVA),
+            ("suz-Sunu", &SUZ_SUNU),
+            ("sv", &SV),
+            ("sv-AX", &SV_AX),
+            ("sv-FI", &SV_FI),
+            ("sw", &SW),
+            ("sw-CD", &SW_CD),
+            ("sw-KE", &SW_KE),
+            ("sw-UG", &SW_UG),
+            ("syr", &SYR),
+            ("syr-SY", &SYR_SY),
+            ("szl", &SZL),
+            ("ta", &TA),
+            ("ta-LK", &TA_LK),
+            ("ta-MY", &TA_MY),
+            ("ta-SG", &TA_SG),
+            ("te", &TE),
+            ("teo", &TEO),
+            ("teo-KE", &TEO_KE),
+            ("tg", &TG),
+            ("th", &TH),
+            ("ti", &TI),
+            ("ti-ER", &TI_ER),
+            ("tig", &TIG),
+            ("tk", &TK),
+            ("tn", &TN),
+            ("tn-BW", &TN_BW),
+            ("to", &TO),
+            ("tok", &TOK),
+            ("tpi", &TPI),
+            ("tr", &TR),
+            ("tr-CY", &TR_CY),
+            ("trv", &TRV),
+            ("trw", &TRW),
+            ("ts", &TS),
+            ("tt", &TT),
+            ("twq", &TWQ),
+            ("tyv", &TYV),
+            ("tzm", &TZM),
+            ("ug", &UG),
+            ("uk", &UK),
+            ("und", &UND),
+            ("ur", &UR),
+            ("ur-IN", &UR_IN),
+            ("uz", &UZ),
+            ("uz-Arab", &UZ_ARAB),
+            ("uz-Cyrl", &UZ_CYRL),
+            ("uz-Latn", &UZ_LATN),
+            ("vai", &VAI),
+            ("vai-Latn", &VAI_LATN),
+            ("vai-Vaii", &VAI_VAII),
+            ("ve", &VE),
+            ("vec", &VEC),
+            ("vi", &VI),
+            ("vmw", &VMW),
+            ("vo", &VO),
+            ("vun", &VUN),
+            ("wa", &WA),
+            ("wae", &WAE),
+            ("wal", &WAL),
+            ("wbp", &WBP),
+            ("wo", &WO),
+            ("xh", &XH),
+            ("xnr", &XNR),
+            ("xog", &XOG),
+            ("yav", &YAV),
+            ("yi", &YI),
+            ("yo", &YO),
+            ("yo-BJ", &YO_BJ),
+            ("yrl", &YRL),
+            ("yrl-CO", &YRL_CO),
+            ("yrl-VE", &YRL_VE),
+            ("yue", &YUE),
+            ("yue-Hans", &YUE_HANS),
+            ("yue-Hant", &YUE_HANT),
+            ("yue-Hant-CN", &YUE_HANT_CN),
+            ("yue-Hant-MO", &YUE_HANT_MO),
+            ("za", &ZA),
+            ("zgh", &ZGH),
+            ("zh", &ZH),
+            ("zh-Hans", &ZH_HANS),
+            ("zh-Hans-HK", &ZH_HANS_HK),
+            ("zh-Hans-MO", &ZH_HANS_MO),
+            ("zh-Hans-MY", &ZH_HANS_MY),
+            ("zh-Hans-SG", &ZH_HANS_SG),
+            ("zh-Hant", &ZH_HANT),
+            ("zh-Hant-HK", &ZH_HANT_HK),
+            ("zh-Hant-MO", &ZH_HANT_MO),
+            ("zh-Hant-MY", &ZH_HANT_MY),
+            ("zh-Latn", &ZH_LATN),
+            ("zu", &ZU),
+        ]
+        .into_iter()
+        .collect()
+    });
diff --git a/rust/pspp/src/locale_charset.rs b/rust/pspp/src/locale_charset.rs
deleted file mode 100644 (file)
index 8f3e5c9..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-// Determine a canonical name for the current locale's character encoding.
-//
-// Copyright (C) 2000-2006, 2008-2023, 2025 Free Software Foundation, Inc.
-//
-// This file is free software: you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation; either version 2.1 of the License, or (at your option)
-// any later version.
-//
-// This file is distributed in the hope that it will be useful, but WITHOUT ANY
-// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-// A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-// details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with this program.  If not, see <https://www.gnu.org/licenses/>.
-//
-// Written by Bruno Haible <bruno@clisp.org>.  Translated to Rust by Ben Pfaff
-// <blp@cs.stanford.edu>.
-
-use std::sync::LazyLock;
-
-fn map_aliases(s: &str) -> &'static str {
-    #[cfg(target_os = "freebsd")]
-    match s {
-        "ARMSCII-8" => return "ARMSCII-8",
-        "Big5" => return "BIG5",
-        "C" => return "ASCII",
-        "CP1131" => return "CP1131",
-        "CP1251" => return "CP1251",
-        "CP866" => return "CP866",
-        "GB18030" => return "GB18030",
-        "GB2312" => return "GB2312",
-        "GBK" => return "GBK",
-        "ISCII-DEV" => return "?",
-        "ISO8859-1" => return "ISO-8859-1",
-        "ISO8859-13" => return "ISO-8859-13",
-        "ISO8859-15" => return "ISO-8859-15",
-        "ISO8859-2" => return "ISO-8859-2",
-        "ISO8859-5" => return "ISO-8859-5",
-        "ISO8859-7" => return "ISO-8859-7",
-        "ISO8859-9" => return "ISO-8859-9",
-        "KOI8-R" => return "KOI8-R",
-        "KOI8-U" => return "KOI8-U",
-        "SJIS" => return "SHIFT_JIS",
-        "US-ASCII" => return "ASCII",
-        "eucCN" => return "GB2312",
-        "eucJP" => return "EUC-JP",
-        "eucKR" => return "EUC-KR",
-        _ => (),
-    };
-
-    #[cfg(target_os = "netbsd")]
-    match s {
-        "646" => return "ASCII",
-        "ARMSCII-8" => return "ARMSCII-8",
-        "BIG5" => return "BIG5",
-        "Big5-HKSCS" => return "BIG5-HKSCS",
-        "CP1251" => return "CP1251",
-        "CP866" => return "CP866",
-        "GB18030" => return "GB18030",
-        "GB2312" => return "GB2312",
-        "ISO8859-1" => return "ISO-8859-1",
-        "ISO8859-13" => return "ISO-8859-13",
-        "ISO8859-15" => return "ISO-8859-15",
-        "ISO8859-2" => return "ISO-8859-2",
-        "ISO8859-4" => return "ISO-8859-4",
-        "ISO8859-5" => return "ISO-8859-5",
-        "ISO8859-7" => return "ISO-8859-7",
-        "KOI8-R" => return "KOI8-R",
-        "KOI8-U" => return "KOI8-U",
-        "PT154" => return "PT154",
-        "SJIS" => return "SHIFT_JIS",
-        "eucCN" => return "GB2312",
-        "eucJP" => return "EUC-JP",
-        "eucKR" => return "EUC-KR",
-        "eucTW" => return "EUC-TW",
-        _ => (),
-    };
-
-    #[cfg(target_os = "openbsd")]
-    match s {
-        "646" => return "ASCII",
-        "ISO8859-1" => return "ISO-8859-1",
-        "ISO8859-13" => return "ISO-8859-13",
-        "ISO8859-15" => return "ISO-8859-15",
-        "ISO8859-2" => return "ISO-8859-2",
-        "ISO8859-4" => return "ISO-8859-4",
-        "ISO8859-5" => return "ISO-8859-5",
-        "ISO8859-7" => return "ISO-8859-7",
-        "US-ASCII" => return "ASCII",
-        _ => (),
-    };
-
-    /* Darwin 7.5 has nl_langinfo(CODESET), but sometimes its value is
-      useless:
-      - It returns the empty string when LANG is set to a locale of the
-        form ll_CC, although ll_CC/LC_CTYPE is a symlink to an UTF-8
-        LC_CTYPE file.
-      - The environment variables LANG, LC_CTYPE, LC_ALL are not set by
-        the system; nl_langinfo(CODESET) returns "US-ASCII" in this case.
-      - The documentation says:
-          "... all code that calls BSD system routines should ensure
-           that the const *char parameters of these routines are in UTF-8
-           encoding. All BSD system functions expect their string
-           parameters to be in UTF-8 encoding and nothing else."
-        It also says
-          "An additional caveat is that string parameters for files,
-           paths, and other file-system entities must be in canonical
-           UTF-8. In a canonical UTF-8 Unicode string, all decomposable
-           characters are decomposed ..."
-        but this is not true: You can pass non-decomposed UTF-8 strings
-        to file system functions, and it is the OS which will convert
-        them to decomposed UTF-8 before accessing the file system.
-      - The Apple Terminal application displays UTF-8 by default.
-      - However, other applications are free to use different encodings:
-        - xterm uses ISO-8859-1 by default.
-        - TextEdit uses MacRoman by default.
-      We prefer UTF-8 over decomposed UTF-8-MAC because one should
-      minimize the use of decomposed Unicode. Unfortunately, through the
-      Darwin file system, decomposed UTF-8 strings are leaked into user
-      space nevertheless.
-      Then there are also the locales with encodings other than US-ASCII
-      and UTF-8. These locales can be occasionally useful to users (e.g.
-      when grepping through ISO-8859-1 encoded text files), when all their
-      file names are in US-ASCII.
-    */
-
-    #[cfg(target_os = "macos")]
-    match s {
-        "ARMSCII-8" => return "ARMSCII-8",
-        "Big5" => return "BIG5",
-        "Big5HKSCS" => return "BIG5-HKSCS",
-        "CP1131" => return "CP1131",
-        "CP1251" => return "CP1251",
-        "CP866" => return "CP866",
-        "CP949" => return "CP949",
-        "GB18030" => return "GB18030",
-        "GB2312" => return "GB2312",
-        "GBK" => return "GBK",
-        "ISO8859-1" => return "ISO-8859-1",
-        "ISO8859-13" => return "ISO-8859-13",
-        "ISO8859-15" => return "ISO-8859-15",
-        "ISO8859-2" => return "ISO-8859-2",
-        "ISO8859-4" => return "ISO-8859-4",
-        "ISO8859-5" => return "ISO-8859-5",
-        "ISO8859-7" => return "ISO-8859-7",
-        "ISO8859-9" => return "ISO-8859-9",
-        "KOI8-R" => return "KOI8-R",
-        "KOI8-U" => return "KOI8-U",
-        "PT154" => return "PT154",
-        "SJIS" => return "SHIFT_JIS",
-        "eucCN" => return "GB2312",
-        "eucJP" => return "EUC-JP",
-        "eucKR" => return "EUC-KR",
-        _ => (),
-    };
-
-    #[cfg(target_os = "aix")]
-    match s {
-        "GBK" => return "GBK",
-        "IBM-1046" => return "CP1046",
-        "IBM-1124" => return "CP1124",
-        "IBM-1129" => return "CP1129",
-        "IBM-1252" => return "CP1252",
-        "IBM-850" => return "CP850",
-        "IBM-856" => return "CP856",
-        "IBM-921" => return "ISO-8859-13",
-        "IBM-922" => return "CP922",
-        "IBM-932" => return "CP932",
-        "IBM-943" => return "CP943",
-        "IBM-eucCN" => return "GB2312",
-        "IBM-eucJP" => return "EUC-JP",
-        "IBM-eucKR" => return "EUC-KR",
-        "IBM-eucTW" => return "EUC-TW",
-        "ISO8859-1" => return "ISO-8859-1",
-        "ISO8859-15" => return "ISO-8859-15",
-        "ISO8859-2" => return "ISO-8859-2",
-        "ISO8859-5" => return "ISO-8859-5",
-        "ISO8859-6" => return "ISO-8859-6",
-        "ISO8859-7" => return "ISO-8859-7",
-        "ISO8859-8" => return "ISO-8859-8",
-        "ISO8859-9" => return "ISO-8859-9",
-        "TIS-620" => return "TIS-620",
-        "UTF-8" => return "UTF-8",
-        "big5" => return "BIG5",
-        _ => (),
-    };
-
-    #[cfg(windows)]
-    match s {
-        "CP1361" => return "JOHAB",
-        "CP20127" => return "ASCII",
-        "CP20866" => return "KOI8-R",
-        "CP20936" => return "GB2312",
-        "CP21866" => return "KOI8-RU",
-        "CP28591" => return "ISO-8859-1",
-        "CP28592" => return "ISO-8859-2",
-        "CP28593" => return "ISO-8859-3",
-        "CP28594" => return "ISO-8859-4",
-        "CP28595" => return "ISO-8859-5",
-        "CP28596" => return "ISO-8859-6",
-        "CP28597" => return "ISO-8859-7",
-        "CP28598" => return "ISO-8859-8",
-        "CP28599" => return "ISO-8859-9",
-        "CP28605" => return "ISO-8859-15",
-        "CP38598" => return "ISO-8859-8",
-        "CP51932" => return "EUC-JP",
-        "CP51936" => return "GB2312",
-        "CP51949" => return "EUC-KR",
-        "CP51950" => return "EUC-TW",
-        "CP54936" => return "GB18030",
-        "CP65001" => return "UTF-8",
-        "CP936" => return "GBK",
-        _ => (),
-    };
-
-    String::from(s).leak()
-}
-
-#[cfg(unix)]
-mod inner {
-    use std::{
-        ffi::{CStr, CString, c_int},
-        ptr::null,
-    };
-
-    use libc::{self, CODESET, LC_CTYPE, nl_langinfo, setlocale};
-
-    unsafe fn string_from_pointer(s: *const i8) -> Option<String> {
-        if s.is_null() {
-            None
-        } else {
-            Some(unsafe { CStr::from_ptr(s).to_string_lossy().into() })
-        }
-    }
-
-    fn set_locale(category: c_int, locale: Option<&str>) -> Option<String> {
-        unsafe {
-            let locale = locale.map(|s| CString::new(s).unwrap());
-            let locale_ptr = locale.as_ref().map_or(null(), |s| s.as_ptr());
-            string_from_pointer(setlocale(category, locale_ptr))
-        }
-    }
-
-    pub fn locale_charset() -> Option<String> {
-        unsafe {
-            let saved_locale = set_locale(LC_CTYPE, None);
-            set_locale(LC_CTYPE, Some(""));
-            let codeset = string_from_pointer(nl_langinfo(CODESET));
-            set_locale(LC_CTYPE, saved_locale.as_deref());
-            codeset
-        }
-    }
-}
-
-#[cfg(windows)]
-mod inner {
-    use libc::{LC_CTYPE, setlocale};
-    use std::ffi::{CStr, CString};
-    use windows_sys::Win32::Globalization::GetACP;
-
-    fn current_locale() -> Option<String> {
-        unsafe {
-            let empty_cstr = CString::new("").unwrap();
-            let locale = setlocale(LC_CTYPE, empty_cstr.as_ptr());
-            if locale.is_null() {
-                None
-            } else {
-                Some(CStr::from_ptr(locale).to_string_lossy().into())
-            }
-        }
-    }
-
-    pub fn locale_charset() -> Option<String> {
-        let Some(current_locale) = current_locale() else {
-            return None;
-        };
-        let codepage = if let Some((_, pdot)) = current_locale.rsplit_once('.') {
-            format!("CP{pdot}")
-        } else {
-            format!("CP{}", unsafe { GetACP() })
-        };
-        Some(match codepage.as_str() {
-            "CP65001" | "CPutf8" => String::from("UTF-8"),
-            _ => codepage,
-        })
-    }
-}
-
-#[cfg(not(any(unix, windows)))]
-mod inner {
-    pub fn locale_charse() -> String {
-        String::from("UTF-8")
-    }
-}
-
-/// Returns the character set used by the locale configured in the operating
-/// system.
-pub fn locale_charset() -> &'static str {
-    static LOCALE_CHARSET: LazyLock<&'static str> =
-        LazyLock::new(|| map_aliases(&inner::locale_charset().unwrap_or(String::from("UTF-8"))));
-    &LOCALE_CHARSET
-}
index 5107842e4bd7ab0ecf80ee34b022ea44eb98e67b..3fa612c1f62834da47dd5a3313706772fa822107 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -321,22 +321,24 @@ impl MacroTokens<'_> {
         self.0.is_empty()
     }
     fn match_(&mut self, s: &str) -> bool {
-        if let Some((first, rest)) = self.0.split_first() {
-            if first.syntax.eq_ignore_ascii_case(s) {
-                self.0 = rest;
-                return true;
-            }
+        if let Some((first, rest)) = self.0.split_first()
+            && first.syntax.eq_ignore_ascii_case(s)
+        {
+            self.0 = rest;
+            true
+        } else {
+            false
         }
-        false
     }
     fn take_relop(&mut self) -> Option<RelOp> {
-        if let Some((first, rest)) = self.0.split_first() {
-            if let Ok(relop) = first.syntax.as_str().try_into() {
-                self.0 = rest;
-                return Some(relop);
-            }
+        if let Some((first, rest)) = self.0.split_first()
+            && let Ok(relop) = first.syntax.as_str().try_into()
+        {
+            self.0 = rest;
+            Some(relop)
+        } else {
+            None
         }
-        None
     }
     fn macro_id(&self) -> Option<&Identifier> {
         self.0.first().and_then(|mt| mt.token.macro_id())
@@ -1533,28 +1535,28 @@ impl Expander<'_> {
 
     fn expand__(&mut self, input: &mut MacroTokens, output: &mut Vec<MacroToken>) {
         // Recursive macro calls.
-        if self.may_expand() {
-            if let Some(call) = Call::for_tokens(self.macros, input.0, &self.error) {
-                let vars = RefCell::new(BTreeMap::new());
-                let mut stack = take(&mut self.stack);
-                stack.push(Frame {
-                    name: Some(call.0.macro_.name.clone()),
-                    location: Some(call.0.macro_.location.clone()),
-                });
-                let mut subexpander = Expander {
-                    break_: None,
-                    vars: &vars,
-                    nesting_countdown: self.nesting_countdown.saturating_sub(1),
-                    stack,
-                    ..*self
-                };
-                let mut body = MacroTokens(call.0.macro_.body.as_slice());
-                subexpander.expand(&mut body, output);
-                self.stack = subexpander.stack;
-                self.stack.pop();
-                input.0 = &input.0[call.len()..];
-                return;
-            }
+        if self.may_expand()
+            && let Some(call) = Call::for_tokens(self.macros, input.0, &self.error)
+        {
+            let vars = RefCell::new(BTreeMap::new());
+            let mut stack = take(&mut self.stack);
+            stack.push(Frame {
+                name: Some(call.0.macro_.name.clone()),
+                location: Some(call.0.macro_.location.clone()),
+            });
+            let mut subexpander = Expander {
+                break_: None,
+                vars: &vars,
+                nesting_countdown: self.nesting_countdown.saturating_sub(1),
+                stack,
+                ..*self
+            };
+            let mut body = MacroTokens(call.0.macro_.body.as_slice());
+            subexpander.expand(&mut body, output);
+            self.stack = subexpander.stack;
+            self.stack.pop();
+            input.0 = &input.0[call.len()..];
+            return;
         }
 
         // Only identifiers beginning with `!` receive further processing.
@@ -1578,12 +1580,12 @@ impl Expander<'_> {
         };
 
         // Macro arguments.
-        if let Some(macro_) = self.macro_ {
-            if let Some(param_idx) = macro_.find_parameter(id) {
-                self.expand_arg(param_idx, output);
-                input.advance();
-                return;
-            }
+        if let Some(macro_) = self.macro_
+            && let Some(param_idx) = macro_.find_parameter(id)
+        {
+            self.expand_arg(param_idx, output);
+            input.advance();
+            return;
         }
 
         // Variables set by `!DO` or `!LET`.
index a1f72129d957c62847e76e6b072ff8a81fd315d8..9cac190f538f25e0ccb66631281bd8869796b114 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
index 125de74687ac7ed5e1c457b8cd68ccbe56505dcf..3fcafb9b71a991a20ad6c9cf5856a889e09d5c44 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -244,10 +244,10 @@ impl Display for Diagnostic {
         {
             let mut prev_line_number = None;
             for (line_number, line) in &self.source {
-                if let Some(prev_line_number) = prev_line_number {
-                    if *line_number != prev_line_number + 1 {
-                        write!(f, "\n  ... |")?;
-                    }
+                if let Some(prev_line_number) = prev_line_number
+                    && *line_number != prev_line_number + 1
+                {
+                    write!(f, "\n  ... |")?;
                 }
                 prev_line_number = Some(line_number);
 
index b2a9277532390bc82223dbac7738088519e8b365..8f9121565407d629b26fff44989a24513546c195 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -18,8 +18,8 @@
 use std::{
     borrow::Cow,
     collections::BTreeMap,
+    convert::Infallible,
     fmt::Display,
-    iter::once,
     mem::take,
     str::FromStr,
     sync::{Arc, OnceLock},
@@ -27,13 +27,13 @@ use std::{
 
 use anyhow::anyhow;
 use bit_vec::BitVec;
-use cairo::ImageSurface;
 use clap::{ArgAction, ArgMatches, Args, FromArgMatches, value_parser};
 use enum_map::EnumMap;
 use enumset::{EnumSet, EnumSetType};
 use itertools::Itertools;
 use pivot::PivotTable;
 use serde::Serialize;
+use smallvec::{SmallVec, smallvec};
 
 use crate::{
     message::{Diagnostic, Severity},
@@ -90,7 +90,7 @@ pub trait Itemlike {
     fn is_shown(&self) -> Option<bool>;
     fn page_break_before(&self) -> bool;
     fn spv_info(&self) -> Option<Cow<'_, SpvInfo>>;
-    fn iter_in_order(&self) -> ItemRefIterator<'_, Self>
+    fn iter_preorder(&self) -> ItemRefIterator<'_, Self>
     where
         Self: Sized;
     fn children(&self) -> &[Arc<Self>];
@@ -177,7 +177,7 @@ impl Itemlike for Item {
             .map(|spv_info| Cow::Borrowed(&**spv_info))
     }
 
-    fn iter_in_order(&self) -> ItemRefIterator<'_, Item> {
+    fn iter_preorder(&self) -> ItemRefIterator<'_, Item> {
         ItemRefIterator::new(self)
     }
 
@@ -340,8 +340,8 @@ impl Display for ItemKind {
 
 #[derive(Clone, Debug, Serialize)]
 pub enum Details {
-    Graph,
-    Image(#[serde(skip_serializing)] ImageSurface),
+    Graph(Graph),
+    Image(#[serde(skip_serializing)] Image),
     Heading(Heading),
     Message(Box<Diagnostic>),
     Table(Box<PivotTable>),
@@ -388,16 +388,16 @@ impl Details {
         }
     }
 
-    pub fn as_image(&self) -> Option<&ImageSurface> {
+    pub fn as_image(&self) -> Option<&Image> {
         match self {
-            Self::Image(image_surface) => Some(image_surface),
+            Self::Image(image) => Some(image),
             _ => None,
         }
     }
 
     pub fn command_name(&self) -> Option<&String> {
         match self {
-            Details::Graph
+            Details::Graph(_)
             | Details::Image(_)
             | Details::Heading(_)
             | Details::Message(_)
@@ -408,7 +408,7 @@ impl Details {
 
     pub fn label(&self) -> Cow<'static, str> {
         match self {
-            Details::Graph => Cow::from("Graph"),
+            Details::Graph(_) => Cow::from("Graph"),
             Details::Image(_) => Cow::from("Image"),
             Details::Heading(_) => Cow::from("Group"),
             Details::Message(diagnostic) => Cow::from(diagnostic.severity.as_title_str()),
@@ -435,7 +435,7 @@ impl Details {
 
     pub fn kind(&self) -> ItemKind {
         match self {
-            Details::Graph => ItemKind::Graph,
+            Details::Graph(_) => ItemKind::Graph,
             Details::Image(_) => ItemKind::Image,
             Details::Heading(_) => ItemKind::Heading,
             Details::Message(diagnostic) => ItemKind::Message(diagnostic.severity),
@@ -496,11 +496,32 @@ impl From<Box<Text>> for Details {
 }
 
 #[derive(Clone, Debug, Serialize)]
-pub struct Graph;
+pub struct Graph {
+    /// XML conforming to VizML schema.
+    pub xml: Vec<u8>,
+
+    /// Legacy binary data in its raw format, usually present.
+    pub data: Option<Vec<u8>>,
+
+    /// CSV file, usually absent.
+    pub csv: Option<Vec<u8>>,
+}
 
 impl Graph {
     pub fn into_item(self) -> Item {
-        Details::Graph.into_item()
+        Details::Graph(self).into_item()
+    }
+}
+
+#[derive(Clone, Debug, Serialize)]
+pub struct Image(
+    /// A PNG image.
+    pub Vec<u8>,
+);
+
+impl Image {
+    pub fn into_item(self) -> Item {
+        Details::Image(self).into_item()
     }
 }
 
@@ -533,7 +554,7 @@ fn text_item_table_look() -> Arc<Look> {
         Arc::new({
             let mut look = Look::default().with_borders(EnumMap::from_fn(|_| BorderStyle::none()));
             for style in look.areas.values_mut() {
-                style.cell_style.margins = EnumMap::from_fn(|_| [0, 0]);
+                style.cell_style.margins = EnumMap::default();
             }
             look
         })
@@ -563,9 +584,15 @@ impl From<&Diagnostic> for Text {
 #[serde(rename_all = "snake_case")]
 pub enum TextType {
     /// `TITLE` and `SUBTITLE` commands.
+    ///
+    /// This kind of text doesn't appear in output directly.  Instead, it
+    /// appears in a page header or footer if `&[PageTitle]` is substituted.
     PageTitle,
 
     /// Title,
+    ///
+    /// This text is a kind of heading or title, presumably above some other
+    /// kind of output.
     Title,
 
     /// Syntax printback logging.
@@ -594,12 +621,16 @@ impl TextType {
     }
 }
 
+/// An iterator for [preorder traversal] of a tree of [Item]s.
+///
+/// [preorder traversal]: https://en.wikipedia.org/wiki/Tree_traversal
 pub struct ItemRefIterator<'a, T> {
     next: Option<&'a T>,
     stack: Vec<(&'a T, usize)>,
 }
 
 impl<'a, T> ItemRefIterator<'a, T> {
+    /// Returns an iterator that omits hidden items.
     pub fn without_hidden(self) -> impl Iterator<Item = &'a T>
     where
         T: Itemlike,
@@ -607,9 +638,10 @@ impl<'a, T> ItemRefIterator<'a, T> {
         self.filter(|item| item.is_shown().unwrap_or(true))
     }
 
-    pub fn new(start: &'a T) -> Self {
+    /// Constructs a new iterator from the given `root`.
+    pub fn new(root: &'a T) -> Self {
         Self {
-            next: Some(start),
+            next: Some(root),
             stack: Vec::new(),
         }
     }
@@ -699,13 +731,18 @@ where
         }
     }
 
+    // Returns an iterator for headings in the stack above the current item.
+    // Level 0 is the top level.
+    pub fn headings(&self) -> impl ExactSizeIterator<Item = &Arc<T>> {
+        self.stack.iter().map(|(item, _index)| item)
+    }
+
     // Returns the label for the heading with the given `level` in the stack
     // above the current item.  Level 0 is the top level.  Levels without a
     // label are skipped.
     pub fn heading(&self, level: usize) -> Option<Cow<'_, str>> {
-        self.stack
-            .iter()
-            .map(|(item, _index)| item.as_ref().label())
+        self.headings()
+            .map(|item| item.as_ref().label())
             .filter(|label| !label.is_empty())
             .nth(level)
     }
@@ -714,26 +751,29 @@ where
 /// Information for output items that were read from an SPV file.
 ///
 /// This is for debugging and troubleshooting purposes.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default)]
 pub struct SpvInfo {
     /// True if there was an error reading the output item (e.g. because of
     /// corruption or because PSPP doesn't understand the format).
     pub error: bool,
 
     /// Name of structure member in ZIP file.
-    pub structure_member: String,
+    ///
+    /// If the output item was not read from a ZIP file, then this is missing.
+    pub structure_member: Option<String>,
 
     /// Additional members based on item type.
     pub members: Option<SpvMembers>,
 }
 
 impl SpvInfo {
-    pub fn new(structure_member: impl Into<String>) -> Self {
-        Self {
-            error: false,
-            structure_member: structure_member.into(),
-            members: None,
-        }
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn with_structure_member(mut self, structure_member: impl Into<String>) -> Self {
+        self.structure_member = Some(structure_member.into());
+        self
     }
 
     pub fn with_members(self, members: Option<SpvMembers>) -> Self {
@@ -745,7 +785,10 @@ impl SpvInfo {
     }
 
     pub fn member_names(&self) -> Vec<&str> {
-        let mut member_names = vec![self.structure_member.as_str()];
+        let mut member_names = Vec::new();
+        if let Some(structure_member) = &self.structure_member {
+            member_names.push(structure_member.as_str());
+        }
         if let Some(members) = &self.members {
             member_names.extend(members.iter());
         }
@@ -774,42 +817,51 @@ pub enum SpvMembers {
         String,
     ),
     /// Graph members.
-    Graph {
-        /// Data member name.
-        data: Option<String>,
-        /// XML member name.
-        xml: String,
-        /// CVS member name.
-        csv: Option<String>,
-    },
+    Graph(SpvGraphMembers),
 }
 
 impl SpvMembers {
     pub fn iter(&self) -> impl Iterator<Item = &str> {
-        let (a, b, c) = match self {
-            SpvMembers::LightTable(a) => (Some(a), None, None),
-            SpvMembers::LegacyTable { xml, binary } => (Some(xml), Some(binary), None),
-            SpvMembers::Image(a) => (Some(a), None, None),
-            SpvMembers::Graph { data, xml, csv } => (data.as_ref(), Some(xml), csv.as_ref()),
+        let members: SmallVec<[&String; 3]> = match self {
+            SpvMembers::LightTable(a) | SpvMembers::Image(a) => smallvec![a],
+            SpvMembers::LegacyTable { xml, binary } => smallvec![xml, binary],
+            SpvMembers::Graph(graph) => [Some(&graph.xml), graph.data.as_ref(), graph.csv.as_ref()]
+                .into_iter()
+                .flatten()
+                .collect(),
         };
-        once(a)
-            .flatten()
-            .chain(once(b).flatten())
-            .chain(once(c).flatten())
-            .map(|s| s.as_str())
+        members.into_iter().map(|s| s.as_str())
     }
 
     /// Returns the name of the binary data member, if any.  Only graphs and
     /// legacy tables have a binary data member.
     pub fn bin_member(&self) -> Option<&str> {
         match self {
-            SpvMembers::LegacyTable { binary, .. } => Some(&binary),
-            SpvMembers::Graph { data: binary, .. } => binary.as_ref().map(|s| s.as_str()),
+            SpvMembers::LegacyTable { binary, .. } => Some(binary),
+            SpvMembers::Graph(graph) => graph.data.as_ref().map(|s| s.as_str()),
+            _ => None,
+        }
+    }
+
+    pub fn graph_members(&self) -> Option<&SpvGraphMembers> {
+        match self {
+            SpvMembers::Graph(spv_graph_members) => Some(&spv_graph_members),
             _ => None,
         }
     }
 }
 
+/// ZIP file members for a graph output item.
+#[derive(Clone, Debug)]
+pub struct SpvGraphMembers {
+    /// Data member name.
+    pub data: Option<String>,
+    /// XML member name.
+    pub xml: String,
+    /// CVS member name.
+    pub csv: Option<String>,
+}
+
 /// Classifications for output items.  These only roughly correspond to the
 /// output item types; for example, "warnings" are a subset of text items.
 #[derive(Debug, EnumSetType)]
@@ -859,11 +911,6 @@ pub struct Selection {
     /// - `Some(false)`: Include only hidden objects.
     pub visible: Option<bool>,
 
-    /// - `None`: Include all objects.
-    /// - `Some(false)`: Include only objects with no error on loading.
-    /// - `Some(true)`: Include only objects with an error on loading.
-    pub error: Option<bool>,
-
     /// Classes to include.
     pub classes: EnumSet<Class>,
 
@@ -949,7 +996,6 @@ impl Default for Selection {
     fn default() -> Self {
         Self {
             visible: Some(true),
-            error: None,
             classes: EnumSet::all(),
             commands: Default::default(),
             subtypes: Default::default(),
@@ -1001,10 +1047,6 @@ impl StringMatch {
     }
 }
 
-/// Can't fail.
-#[derive(Debug, thiserror::Error)]
-pub enum Infallible {}
-
 impl FromStr for StringMatch {
     type Err = Infallible;
 
@@ -1023,7 +1065,7 @@ pub struct Criteria(pub Vec<Selection>);
 impl Criteria {
     /// Returns output items that are a subset of `input` that match the
     /// criteria.
-    pub fn apply<T>(&self, input: Vec<T>) -> Vec<Arc<T>>
+    pub fn apply<T>(&self, input: Vec<T>) -> Vec<T>
     where
         T: Itemlike + Clone,
     {
@@ -1055,7 +1097,7 @@ impl Criteria {
             let mut command_item = None;
             let mut command_command_item = None;
             let mut nth_command = 0;
-            for (index, (item, depth)) in items.into_iter().enumerate() {
+            for (index, (item, depth)) in items.iter().enumerate() {
                 if *depth == 0 {
                     command_item = Some(index);
                     if let Some(last_instance) = last_instance.take() {
@@ -1072,11 +1114,6 @@ impl Criteria {
                 {
                     continue;
                 }
-                if let Some(error) = selection.error
-                    && error != item.spv_info().map_or(false, |spv_info| spv_info.error)
-                {
-                    continue;
-                }
                 if !selection
                     .commands
                     .matches(item.command_name().unwrap_or(""))
@@ -1085,7 +1122,7 @@ impl Criteria {
                 }
                 if !selection.nth_commands.is_empty() {
                     if command_item != command_command_item {
-                        command_command_item = command_command_item;
+                        command_command_item = command_item;
                         nth_command += 1;
                     }
                     if !selection.nth_commands.contains(&nth_command) {
@@ -1179,7 +1216,7 @@ impl Criteria {
             &mut include.iter(),
             &mut output,
         );
-        output
+        output.into_iter().map(Arc::unwrap_or_clone).collect()
     }
 }
 
@@ -1201,7 +1238,7 @@ impl FromArgMatches for Criteria {
             NthCommands(Vec<usize>),
             Instances(Vec<usize>),
             ShowHidden(bool),
-            Errors(bool),
+            Members(String),
         }
 
         fn extract<F, T: Clone + Send + Sync + 'static>(
@@ -1240,7 +1277,7 @@ impl FromArgMatches for Criteria {
         extract(matches, "nth_commands", &mut values, Value::NthCommands);
         extract(matches, "instances", &mut values, Value::Instances);
         extract(matches, "show_hidden", &mut values, Value::ShowHidden);
-        extract(matches, "errors", &mut values, Value::Errors);
+        extract(matches, "members", &mut values, Value::Members);
 
         if !values.is_empty() {
             let mut selection = Selection::default();
@@ -1256,7 +1293,7 @@ impl FromArgMatches for Criteria {
                     Value::ShowHidden(show) => {
                         selection.visible = if show { None } else { Some(true) }
                     }
-                    Value::Errors(only) => selection.error = if only { Some(true) } else { None },
+                    Value::Members(member) => selection.members.push(member),
                 }
             }
             self.0.push(selection);
index 7e2287972e69048a171bf20da67251810c03f717..76f7abd26abb7df35f5612fe768c6a3e12b09722 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -97,13 +97,6 @@ pub trait Driver {
     fn handles_show(&self) -> bool {
         false
     }
-
-    /// Ordinarily, the core driver code will flatten groups of output items
-    /// before passing them to [Self::write].  If this returns true, the core
-    /// driver code leaves them in place for the driver to handle.
-    fn handles_groups(&self) -> bool {
-        false
-    }
 }
 
 impl Driver for Box<dyn Driver> {
@@ -127,10 +120,6 @@ impl Driver for Box<dyn Driver> {
         (**self).handles_show()
     }
 
-    fn handles_groups(&self) -> bool {
-        (**self).handles_groups()
-    }
-
     fn can_serialize(&self) -> bool {
         (**self).can_serialize()
     }
index f64fe1bf37c17c845e87cf63044e4609af290581..7494d1741a562bdd095331cf01e03c02841c31fd 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -14,6 +14,7 @@
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
+use cairo::Context;
 use pango::SCALE;
 
 use crate::output::pivot::look::HorzAlign;
@@ -29,10 +30,18 @@ fn px_to_xr(x: isize) -> isize {
     x * 3 * (SCALE as isize * 72 / 96) / 3
 }
 
-fn xr_to_pt(x: isize) -> f64 {
+pub fn pt_to_xr(x: f64) -> isize {
+    (x * SCALE as f64 + 0.5) as isize
+}
+
+pub fn xr_to_pt(x: isize) -> f64 {
     x as f64 / SCALE as f64
 }
 
+fn xr_to_px(x: isize) -> f64 {
+    x as f64 * 0.75 / SCALE as f64
+}
+
 impl From<HorzAlign> for pango::Alignment {
     fn from(horz_align: HorzAlign) -> Self {
         match horz_align {
@@ -52,3 +61,13 @@ mod tests {
         CairoDriver::new(&CairoConfig::new("test.pdf")).unwrap();
     }
 }
+
+fn with_saved_context<F, R>(context: &Context, f: F) -> R
+where
+    F: FnOnce() -> R,
+{
+    let _ = context.save();
+    let result = f();
+    let _ = context.restore();
+    result
+}
index 623b4631f9a0621c121c4fb4e8f4bd19c277c472..0d534acbe63295f1ce4a2fb8d48b27da699f18b6 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -114,6 +114,7 @@ impl CairoDriver {
             fg: Color::BLACK,
             use_system_colors: false,
             object_spacing: scale(page_setup.object_spacing.into_unit(Unit::Inch)),
+            chart_size: page_setup.chart_size,
             font_resolution: 72.0,
         };
         let (width, height) = page_setup.paper.as_unit(Unit::Point).into_width_height();
index 5e5738d6ac2333b99a3372d58d9b7640bfbb7128..9683764eca81283fa0679cdc59a8c739229e1ddc 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{cmp::min, f64::consts::PI, fmt::Write, ops::DerefMut, sync::Arc};
+use std::{cmp::min, f64::consts::PI, fmt::Write, io::Cursor, ops::DerefMut, sync::Arc};
 
-use cairo::Context;
+use cairo::{Context, ImageSurface, Rectangle};
 use enum_map::{EnumMap, enum_map};
+use gio::ReadInputStream;
 use itertools::Itertools;
+use num::NumCast;
 use pango::{
     AttrFloat, AttrFontDesc, AttrInt, AttrList, Attribute, FontDescription, FontMask, Layout,
     SCALE, SCALE_SMALL, Underline, Weight,
 };
 use pangocairo::functions::show_layout;
+use rsvg::{CairoRenderer, Loader, SvgHandle};
 use smallvec::{SmallVec, smallvec};
 
 use crate::{
     output::{
-        Details, Item,
-        drivers::cairo::{px_to_xr, xr_to_pt},
+        Details, Graph, Item,
+        drivers::cairo::{px_to_xr, with_saved_context, xr_to_pt, xr_to_px},
+        page::ChartSize,
         pivot::{
             Axis2, Coord2, PivotTable, Rect2,
             look::{BorderStyle, Color, FontStyle, HorzAlign, Stroke},
@@ -51,9 +55,41 @@ fn pxf_to_xr(x: f64) -> isize {
     (x * (SCALE as f64 * 72.0 / 96.0)).round() as isize
 }
 
+pub trait Fsm {
+    fn draw_slice(
+        &mut self,
+        context: &Context,
+        style: &CairoFsmStyle,
+        space: isize,
+        max_space: isize,
+    ) -> isize;
+    fn is_done(&self) -> bool;
+}
+
+impl dyn Fsm {
+    pub fn new(item: Arc<Item>, style: &CairoFsmStyle, context: &Context) -> Option<Box<Self>> {
+        match &item.details {
+            Details::Graph(graph) => Some(Box::new(GraphFsm::new(graph))),
+            Details::Image(image) => Some(Box::new(ImageFsm::new(&image.0))),
+            Details::Heading(_heading) => None,
+            Details::Message(_diagnostic) => None,
+            Details::Table(_) => Some(Box::new(TableFsm::new(style, true, context, item))),
+            Details::Text(text) => {
+                let item = Arc::new(Item::new(PivotTable::from((&**text).clone())));
+                Some(Box::new(TableFsm::new(style, true, context, item)))
+            }
+        }
+    }
+}
+
 #[derive(Clone, Debug)]
 pub struct CairoFsmStyle {
-    /// Page size.
+    /// Page size:
+    ///
+    /// - Excluding margins.
+    ///
+    /// - Including the header and footer (if any), because their sizes can vary
+    ///   from one page to the next.
     pub size: Coord2,
 
     /// Minimum cell size to allow breaking.
@@ -71,6 +107,9 @@ pub struct CairoFsmStyle {
     /// Vertical space between different output items.
     pub object_spacing: isize,
 
+    /// Size of charts.
+    pub chart_size: ChartSize,
+
     /// Resolution, in units per inch, used for measuring font "points":
     ///
     /// - 72.0 if 1 pt is one device unit, e.g. for rendering to a surface
@@ -89,21 +128,159 @@ impl CairoFsmStyle {
     }
 }
 
-pub struct CairoFsm {
-    style: Arc<CairoFsmStyle>,
+struct GraphFsm {
+    svg: SvgHandle,
+    done: bool,
+}
+
+impl GraphFsm {
+    fn new(graph: &Graph) -> Self {
+        let svg = graph.to_svg(None, |w| eprintln!("{w}")).unwrap(); // XXX
+        let svg = svg.to_string();
+        let loader = Loader::new();
+        let stream = ReadInputStream::new(Cursor::new(svg));
+        let svg = loader
+            .read_stream(&stream, gio::File::NONE, gio::Cancellable::NONE)
+            .unwrap();
+        Self { svg, done: false }
+    }
+}
+
+impl Fsm for GraphFsm {
+    fn draw_slice(
+        &mut self,
+        context: &Context,
+        style: &CairoFsmStyle,
+        space: isize,
+        max_space: isize,
+    ) -> isize {
+        let renderer = CairoRenderer::new(&self.svg);
+        let (natural_width, natural_height) = renderer
+            .intrinsic_size_in_pixels()
+            .and_then(|(w, h)| {
+                let w: Option<isize> = NumCast::from(w.ceil());
+                let h: Option<isize> = NumCast::from(h.ceil());
+                w.zip(h)
+            })
+            .unwrap_or((400, 300));
+        let natural_width = px_to_xr(natural_width);
+        let natural_height = px_to_xr(natural_height);
+        let target_height = match style.chart_size {
+            ChartSize::AsIs => natural_height.min(max_space),
+            ChartSize::FullHeight => max_space,
+            ChartSize::HalfHeight => (max_space - style.object_spacing) / 2,
+            ChartSize::QuarterHeight => (max_space - style.object_spacing * 3) / 4,
+        };
+        let target_height = if target_height < 0 {
+            natural_height.min(max_space)
+        } else {
+            target_height
+        };
+
+        // Figure out the scale to use to achieve `target_height`.
+        let scale = target_height as f64 / natural_height as f64;
+        let scaled_width = (natural_width as f64 * scale) as isize;
+
+        // Reduce the scale, if necessary, to keep from exceeding the width.
+        let scale = if scaled_width <= style.size.x() {
+            // The graph fits in the space available.
+            scale
+        } else {
+            scale * (style.size.x() as f64 / scaled_width as f64)
+        };
+
+        let final_width = natural_width as f64 * scale;
+        let final_height = natural_height as f64 * scale;
+        if final_height as isize > space {
+            return 0;
+        }
+
+        let xr_width = xr_to_px(final_width as isize);
+        let xr_height = xr_to_px(final_height as isize);
+        context.scale(scale, scale);
+        context.rectangle(0.0, 0.0, xr_width, xr_height);
+        context.clip();
+        renderer
+            .render_document(&context, &Rectangle::new(0.0, 0.0, xr_width, xr_height))
+            .unwrap();
+        self.done = true;
+
+        (final_height * 0.75) as isize
+    }
+
+    fn is_done(&self) -> bool {
+        self.done
+    }
+}
+
+struct ImageFsm {
+    image: ImageSurface,
+    done: bool,
+}
+
+impl ImageFsm {
+    fn new(image: &[u8]) -> Self {
+        let image = ImageSurface::create_from_png(&mut Cursor::new(image)).unwrap(); // XXX
+        Self { image, done: false }
+    }
+}
+
+impl Fsm for ImageFsm {
+    fn draw_slice(
+        &mut self,
+        context: &Context,
+        style: &CairoFsmStyle,
+        space: isize,
+        max_space: isize,
+    ) -> isize {
+        let width = px_to_xr(self.image.width() as isize);
+        let scale = if style.size.x() < width {
+            style.size.x() as f64 / width as f64
+        } else {
+            1.0
+        };
+        let height = px_to_xr(self.image.height() as isize);
+        let scaled_height = (height as f64 * scale) as isize;
+        let scale = if scaled_height <= space {
+            // The image fits in the space available.
+            scale
+        } else if space >= max_space {
+            // The image doesn't fit in the space available, but the space
+            // available is a full page, so we're not going to do better.  Scale
+            // the image to fit in the available space.
+            scale * (space as f64 / scaled_height as f64)
+        } else {
+            // The image doesn't fit in the space remaining on this page.  We'd
+            // have more space on a new page, so ask for a new page.
+            return 0;
+        };
+
+        self.done = true;
+
+        // XXX Scaling converts 1/96" px to 1/72" pt
+        context.scale(scale * 0.75, scale * 0.75);
+
+        context.set_source_surface(&self.image, 0.0, 0.0).unwrap();
+        context.rectangle(0.0, 0.0, width as f64, height as f64);
+        context.clip();
+        context.paint().unwrap();
+        (height as f64 * scale) as isize
+    }
+
+    fn is_done(&self) -> bool {
+        self.done
+    }
+}
+
+struct TableFsm {
     params: Params,
     item: Arc<Item>,
-    layer_iterator: Option<Box<dyn Iterator<Item = SmallVec<[usize; 4]>>>>,
-    pager: Option<Pager>,
+    layer_iterator: Box<dyn Iterator<Item = SmallVec<[usize; 4]>>>,
+    pager: Pager,
 }
 
-impl CairoFsm {
-    pub fn new(
-        style: Arc<CairoFsmStyle>,
-        printing: bool,
-        context: &Context,
-        item: Arc<Item>,
-    ) -> Self {
+impl TableFsm {
+    pub fn new(style: &CairoFsmStyle, printing: bool, context: &Context, item: Arc<Item>) -> Self {
         let params = Params {
             size: style.size,
             font_size: {
@@ -136,83 +313,55 @@ impl CairoFsm {
             params: &params,
             context,
         };
-        let item = if let Some(text) = item.details.as_text() {
-            Arc::new(Item::new(PivotTable::from(text.clone())))
-        } else {
-            item
-        };
-        let (layer_iterator, pager) = match &item.details {
-            Details::Table(pivot_table) => {
-                let mut layer_iterator = pivot_table.layers(printing);
-                let layer_indexes = layer_iterator.next();
-                (
-                    Some(layer_iterator),
-                    Some(Pager::new(
-                        &device,
-                        pivot_table,
-                        layer_indexes.as_ref().map(|indexes| indexes.as_slice()),
-                    )),
-                )
-            }
-            _ => (None, None),
-        };
+        let pivot_table = item.details.as_table().unwrap();
+        let mut layer_iterator = pivot_table.layers(printing);
+        let layer_indexes = layer_iterator.next();
+        let pager = Pager::new(
+            &device,
+            pivot_table,
+            layer_indexes.as_ref().map(|indexes| indexes.as_slice()),
+        );
         Self {
-            style,
             params,
             item,
             layer_iterator,
             pager,
         }
     }
+}
 
-    pub fn draw_slice(&mut self, context: &Context, space: isize) -> isize {
+impl Fsm for TableFsm {
+    fn draw_slice(
+        &mut self,
+        context: &Context,
+        style: &CairoFsmStyle,
+        space: isize,
+        _max_space: isize,
+    ) -> isize {
         debug_assert!(self.params.printing);
 
-        context.save().unwrap();
-        let used = match &self.item.details {
-            Details::Table(_) => self.draw_table(context, space),
-            _ => 0,
-        };
-        context.restore().unwrap();
-
-        used
-    }
-
-    fn draw_table(&mut self, context: &Context, space: isize) -> isize {
-        let pivot_table = self.item.details.as_table().unwrap();
-        let Some(pager) = &mut self.pager else {
-            return 0;
-        };
         let mut device = CairoDevice {
-            style: &self.style,
+            style,
             params: &self.params,
-            context,
+            context: context,
         };
-        let mut used = pager.draw_next(&mut device, space);
-        if pager.has_next(&device).is_none() {
-            if let Some(layer_indexes) = self.layer_iterator.as_mut().unwrap().next() {
-                self.pager = Some(Pager::new(
-                    &device,
-                    pivot_table,
-                    Some(layer_indexes.as_slice()),
-                ));
-                if pivot_table.style.look.paginate_layers {
-                    used = space;
-                } else {
-                    used += self.style.object_spacing;
-                }
+        let mut used = self.pager.draw_next(&mut device, space);
+        let pivot_table = self.item.details.as_table().unwrap();
+        while self.pager.get_next(&device).is_none()
+            && let Some(layer_indexes) = self.layer_iterator.next()
+        {
+            self.pager = Pager::new(&device, pivot_table, Some(layer_indexes.as_slice()));
+            if pivot_table.style.look.paginate_layers {
+                used = space;
             } else {
-                self.pager = None;
+                used += style.object_spacing;
             }
         }
         used.min(space)
     }
 
-    pub fn is_done(&self) -> bool {
-        match &self.item.details {
-            Details::Table(_) => self.pager.is_none(),
-            _ => true,
-        }
+    fn is_done(&self) -> bool {
+        !self.pager.has_next()
     }
 }
 
@@ -257,7 +406,7 @@ fn margin(cell: &DrawCell, axis: Axis2) -> isize {
 }
 
 pub fn parse_font_style(font_style: &FontStyle) -> FontDescription {
-    let font = font_style.font.as_str();
+    let font = &*font_style.font;
     let font = if font.eq_ignore_ascii_case("Monospaced") {
         "Monospace"
     } else {
@@ -325,7 +474,7 @@ impl<'a, 'b> DrawCell<'a, 'b> {
 
         let parsed_font;
         let font = if !self.font_style.font.is_empty() {
-            parsed_font = parse_font_style(&self.font_style);
+            parsed_font = parse_font_style(self.font_style);
             &parsed_font
         } else {
             default_font
@@ -443,25 +592,25 @@ impl<'a, 'b> DrawCell<'a, 'b> {
         clip: Option<&Rect2>,
         context: &Context,
     ) {
-        context.save().unwrap();
-        if !self.rotate
-            && let Some(clip) = clip
-        {
-            xr_clip(context, clip);
-        }
-        if self.rotate {
-            let extra = (bb[Axis2::X].len() as isize - layout.size().1 as isize).max(0);
-            let halign_offset = extra / 2;
-            context.translate(
-                xr_to_pt(bb[Axis2::X].start + halign_offset),
-                xr_to_pt(bb[Axis2::Y].end),
-            );
-            context.rotate(-PI / 2.0);
-        } else {
-            context.translate(xr_to_pt(bb[Axis2::X].start), xr_to_pt(bb[Axis2::Y].start));
-        }
-        show_layout(context, &layout);
-        context.restore().unwrap();
+        with_saved_context(context, || {
+            if !self.rotate
+                && let Some(clip) = clip
+            {
+                xr_clip(context, clip);
+            }
+            if self.rotate {
+                let extra = (bb[Axis2::X].len() as isize - layout.size().1 as isize).max(0);
+                let halign_offset = extra / 2;
+                context.translate(
+                    xr_to_pt(bb[Axis2::X].start + halign_offset),
+                    xr_to_pt(bb[Axis2::Y].end),
+                );
+                context.rotate(-PI / 2.0);
+            } else {
+                context.translate(xr_to_pt(bb[Axis2::X].start), xr_to_pt(bb[Axis2::Y].start));
+            }
+            show_layout(context, layout);
+        });
     }
 }
 
@@ -482,7 +631,7 @@ impl CairoDevice<'_> {
     fn cell_draw(&self, cell: &DrawCell, bb: Rect2, clip: &Rect2) {
         let mut layout = self.style.new_layout(self.context);
         cell.layout(&bb, &mut layout, &self.style.font);
-        cell.draw(&bb, &layout, Some(clip), &self.context);
+        cell.draw(&bb, &layout, Some(clip), self.context);
     }
 
     fn do_draw_line(
@@ -712,44 +861,44 @@ impl Device for CairoDevice<'_> {
     ) {
         let bg = draw_cell.font_style.bg;
         if (bg.r != 255 || bg.g != 255 || bg.b != 255) && bg.alpha != 0 {
-            self.context.save().unwrap();
-            let bg_clip = Rect2::from_fn(|axis| {
-                let start = if bb[axis].start == clip[axis].start {
-                    clip[axis].start.saturating_sub(spill[axis][0])
-                } else {
-                    clip[axis].start
-                };
-                let end = if bb[axis].end == clip[axis].end {
-                    clip[axis].end + spill[axis][1]
-                } else {
-                    clip[axis].end
-                };
-                start..end
+            with_saved_context(self.context, || {
+                let bg_clip = Rect2::from_fn(|axis| {
+                    let start = if bb[axis].start == clip[axis].start {
+                        clip[axis].start.saturating_sub(spill[axis][0])
+                    } else {
+                        clip[axis].start
+                    };
+                    let end = if bb[axis].end == clip[axis].end {
+                        clip[axis].end + spill[axis][1]
+                    } else {
+                        clip[axis].end
+                    };
+                    start..end
+                });
+                xr_clip(self.context, &bg_clip);
+                xr_set_color(self.context, bg);
+                let x0 = bb[Axis2::X].start.saturating_sub(spill[Axis2::X][0]);
+                let y0 = bb[Axis2::Y].start.saturating_sub(spill[Axis2::X][1]);
+                let x1 = bb[Axis2::X].end + spill[Axis2::X][1];
+                let y1 = bb[Axis2::Y].end + spill[Axis2::Y][1];
+                xr_fill_rectangle(self.context, Rect2::new(x0..x1, y0..y1));
             });
-            xr_clip(self.context, &bg_clip);
-            xr_set_color(self.context, bg);
-            let x0 = bb[Axis2::X].start.saturating_sub(spill[Axis2::X][0]);
-            let y0 = bb[Axis2::Y].start.saturating_sub(spill[Axis2::X][1]);
-            let x1 = bb[Axis2::X].end + spill[Axis2::X][1];
-            let y1 = bb[Axis2::Y].end + spill[Axis2::Y][1];
-            xr_fill_rectangle(self.context, Rect2::new(x0..x1, y0..y1));
-            self.context.restore().unwrap();
         }
 
         if !self.style.use_system_colors {
             xr_set_color(self.context, draw_cell.font_style.fg);
         }
 
-        self.context.save().unwrap();
-        bb[Axis2::Y].start += valign_offset;
-        for axis in [Axis2::X, Axis2::Y] {
-            bb[axis].start += px_to_xr(draw_cell.cell_style.margins[axis][0] as isize);
-            bb[axis].end -= px_to_xr(draw_cell.cell_style.margins[axis][1] as isize);
-        }
-        if bb[Axis2::X].start < bb[Axis2::X].end && bb[Axis2::Y].start < bb[Axis2::Y].end {
-            self.cell_draw(draw_cell, bb, clip);
-        }
-        self.context.restore().unwrap();
+        with_saved_context(self.context, || {
+            bb[Axis2::Y].start += valign_offset;
+            for axis in [Axis2::X, Axis2::Y] {
+                bb[axis].start += px_to_xr(draw_cell.cell_style.margins[axis][0] as isize);
+                bb[axis].end -= px_to_xr(draw_cell.cell_style.margins[axis][1] as isize);
+            }
+            if bb[Axis2::X].start < bb[Axis2::X].end && bb[Axis2::Y].start < bb[Axis2::Y].end {
+                self.cell_draw(draw_cell, bb, clip);
+            }
+        });
     }
 
     fn scale(&mut self, factor: f64) {
index 5ae677e19d8af4af6d086880ef0812853b37466d..cd21924e249ec15dba8910647d1ace776b7e654d 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{borrow::Cow, sync::Arc};
+use std::{borrow::Cow, ops::Range, sync::Arc};
 
 use cairo::{Context, RecordingSurface};
 use enum_map::EnumMap;
@@ -24,8 +24,8 @@ use crate::{
     output::{
         Item,
         drivers::cairo::{
-            fsm::{CairoFsm, CairoFsmStyle},
-            xr_to_pt,
+            fsm::{CairoFsmStyle, Fsm},
+            with_saved_context, xr_to_pt,
         },
         pivot::{
             Axis2, Rect2,
@@ -51,9 +51,9 @@ pub struct CairoPager {
     page_index: i32,
     item: Option<Arc<Item>>,
     context: Option<Context>,
-    fsm: Option<CairoFsm>,
+    fsm: Option<Box<dyn Fsm>>,
     y: isize,
-    y_max: isize,
+    y_body: Range<isize>,
 }
 
 impl CairoPager {
@@ -66,7 +66,7 @@ impl CairoPager {
             context: None,
             fsm: None,
             y: 0,
-            y_max: 0,
+            y_body: 0..0,
         }
     }
 
@@ -103,9 +103,9 @@ impl CairoPager {
         // Render footer.
         let footer = header.with_heading(&self.page_style.footer);
         let footer_size = footer.measure();
-        self.y_max = self.fsm_style.size[Axis2::Y] - footer_size;
+        self.y_body = self.y..self.fsm_style.size[Axis2::Y] - footer_size;
         if footer_size > 0 {
-            footer.render(&context, self.y_max);
+            footer.render(&context, self.y_body.end);
         }
 
         context.translate(0.0, xr_to_pt(self.y));
@@ -122,7 +122,7 @@ impl CairoPager {
 
     pub fn needs_new_page(&self) -> bool {
         (self.item.is_some() || self.fsm.is_some())
-            && (self.context.is_none() || self.y >= self.y_max)
+            && (self.context.is_none() || self.y >= self.y_body.end)
     }
 
     pub fn add_item(&mut self, item: Arc<Item>) {
@@ -140,19 +140,26 @@ impl CairoPager {
 
         loop {
             // Make sure we've got an object to render.
-            let fsm = match &mut self.fsm {
-                Some(fsm) => fsm,
-                None => {
-                    let Some(item) = self.item.take() else {
-                        return;
-                    };
-                    self.fsm
-                        .insert(CairoFsm::new(self.fsm_style.clone(), true, &context, item))
-                }
+            let fsm = if let Some(fsm) = &mut self.fsm {
+                fsm
+            } else if let Some(item) = self.item.take()
+                && let Some(fsm) = <dyn Fsm>::new(item, &self.fsm_style, &context)
+            {
+                self.fsm.insert(fsm)
+            } else {
+                return;
             };
 
             // Prepare to render the current object.
-            let chunk = fsm.draw_slice(&context, (self.y_max - self.y).max(0));
+            let space = (self.y_body.end - self.y).max(0);
+            let chunk = with_saved_context(&context, || {
+                fsm.draw_slice(
+                    &context,
+                    &self.fsm_style,
+                    space,
+                    self.y_body.len().try_into().unwrap(),
+                )
+            });
             self.y += chunk + self.fsm_style.object_spacing;
             context.translate(0.0, xr_to_pt(chunk + self.fsm_style.object_spacing));
 
@@ -202,7 +209,6 @@ where
             &|variable| Some(Cow::from((self.substitutions)(variable, self.page_number)));
 
         for block in self.heading.to_values() {
-            // XXX substitute heading variables
             let cell = DrawCell {
                 rotate: false,
                 inner: &block.inner,
index 801ebfd9e74fda062f9fdeff2760e17a6f27175b..796bc9601b48a244166ef7522051a8a639a352cd 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -360,7 +360,7 @@ impl Driver for CsvDriver {
     fn write(&mut self, item: &Arc<Item>) {
         // todo: error handling (should not unwrap)
         match &item.details {
-            Details::Graph | Details::Image(_) | Details::Heading(_) => (),
+            Details::Graph(_) | Details::Image(_) | Details::Heading(_) => (),
             Details::Message(diagnostic) => {
                 self.start_item();
                 let text = diagnostic.to_string();
index a057364a4ed89c063eb962a9c9db537c3cae3fce..c2ea4049171c35e1fd08c789d7fc293dbb25548a 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -19,21 +19,30 @@ use std::{
     fmt::{Display, Write as _},
     fs::File,
     io::Write,
+    ops::Not,
     path::PathBuf,
     sync::Arc,
 };
 
+use base64::{display::Base64Display, engine::general_purpose::STANDARD};
+use quick_xml::{
+    Writer,
+    events::{BytesEnd, BytesStart, BytesText, Event},
+};
 use serde::{Deserialize, Serialize};
-use smallstr::SmallString;
-
-use crate::output::{
-    Details, Item,
-    drivers::Driver,
-    pivot::{
-        Axis2, PivotTable,
-        look::{BorderStyle, Color, HorzAlign, Stroke, VertAlign},
+
+use crate::{
+    output::{
+        Details, Graph, Item, TextType,
+        drivers::Driver,
+        pivot::{
+            Axis2, OutputTables, PivotTable,
+            look::{BorderStyle, Color, HorzAlign, Stroke, VertAlign},
+            value::ValueOptions,
+        },
+        table::{CellPos, CellRect, DrawCell, Table},
     },
-    table::{CellPos, CellRect, DrawCell, Table},
+    util::ToSmallString,
 };
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
@@ -41,8 +50,11 @@ pub struct HtmlConfig {
     file: PathBuf,
 }
 
-pub struct HtmlDriver<W> {
-    writer: W,
+pub struct HtmlDriver<W>
+where
+    W: Write,
+{
+    writer: Option<Writer<W>>,
     fg: Color,
     bg: Color,
 }
@@ -62,7 +74,7 @@ impl Stroke {
 
 impl HtmlDriver<File> {
     pub fn new(config: &HtmlConfig) -> std::io::Result<Self> {
-        Ok(Self::for_writer(File::create(&config.file)?))
+        Ok(Self::for_writer(File::create(&config.file)?)?)
     }
 }
 
@@ -70,182 +82,339 @@ impl<W> HtmlDriver<W>
 where
     W: Write,
 {
-    pub fn for_writer(mut writer: W) -> Self {
-        let _ = put_header(&mut writer);
-        Self {
+    pub fn for_writer(writer: W) -> std::io::Result<Self> {
+        let mut writer = Writer::new(writer);
+        writer.write_event(Event::DocType(BytesText::new("HTML")))?;
+        writer.write_event(Event::Start(BytesStart::new("html")))?;
+        writer.create_element("head").write_inner_content(|w| {
+            w.create_element("title")
+                .write_text_content(BytesText::new("PSPP Output"))?;
+            w.create_element("meta")
+                .with_attribute(("name", "generator"))
+                .with_attribute(("content", env!("CARGO_PKG_VERSION")))
+                .write_empty()?;
+            w.create_element("meta")
+                .with_attribute(("http-equiv", "content-type"))
+                .with_attribute(("content", "text/html; charset=utf8"))
+                .write_empty()?;
+            w.create_element("style")
+                .write_text_content(BytesText::new(CSS))?;
+            Ok(())
+        })?;
+        writer.write_event(Event::Start(BytesStart::new("body")))?;
+        Ok(Self {
             fg: Color::BLACK,
             bg: Color::WHITE,
-            writer,
+            writer: Some(writer),
+        })
+    }
+
+    pub fn close(&mut self) -> std::io::Result<()> {
+        if let Some(mut writer) = self.writer.take() {
+            writer.write_event(Event::End(BytesEnd::new("body")))?;
+            writer.write_event(Event::End(BytesEnd::new("html")))?;
+            writer.into_inner().flush()
+        } else {
+            Ok(())
         }
     }
 
-    fn render(&mut self, pivot_table: &PivotTable) -> std::io::Result<()> {
-        for layer_indexes in pivot_table.layers(true) {
-            let output = pivot_table.output(&layer_indexes, false);
-            write!(&mut self.writer, "<table")?;
-            if let Some(notes) = &pivot_table.metadata.notes {
-                write!(&mut self.writer, r#" title="{}""#, Escape::new(notes))?;
-            }
-            writeln!(&mut self.writer, ">")?;
-
-            if let Some(title) = output.title {
-                let cell = title.get(CellPos::new(0, 0));
-                self.put_cell(
-                    DrawCell::new(cell.inner(), &title),
-                    CellRect::new(0..1, 0..1),
-                    "caption",
-                    None,
-                )?;
-            }
+    fn write_layer(
+        &mut self,
+        pivot_table: &PivotTable,
+        output: OutputTables,
+    ) -> std::io::Result<()> {
+        self.writer
+            .as_mut()
+            .unwrap()
+            .create_element("table")
+            .with_attributes(
+                pivot_table
+                    .metadata
+                    .notes
+                    .as_ref()
+                    .map(|notes| ("title", notes.as_str())),
+            )
+            .write_inner_content(|w| {
+                // Write title as <caption>.
+                if let Some(title) = output.title {
+                    let cell = title.get(CellPos::new(0, 0));
+                    write_cell(w, DrawCell::new(cell.inner(), &title), "caption", None)?;
+                }
 
-            if !output.layers.is_empty() {
-                writeln!(&mut self.writer, "<thead>")?;
-                for layer in &output.layers {
-                    writeln!(&mut self.writer, "<tr>")?;
-                    for cell in layer.cells() {
-                        self.put_cell(
-                            DrawCell::new(cell.inner(), layer),
-                            CellRect::for_cell(cell.pos),
-                            "td",
-                            None,
-                        )?;
-                    }
-                    writeln!(&mut self.writer, "</tr>")?;
+                // Write layer values as <thead>.
+                if !output.layers.is_empty() {
+                    w.create_element("thead").write_inner_content(|w| {
+                        for layer in &output.layers {
+                            w.create_element("tr").write_inner_content(|w| {
+                                for cell in layer.cells() {
+                                    write_cell(
+                                        w,
+                                        DrawCell::new(cell.inner(), layer),
+                                        "td",
+                                        Some((CellRect::for_cell(cell.pos), None)),
+                                    )?;
+                                }
+                                Ok(())
+                            })?;
+                        }
+                        Ok(())
+                    })?;
                 }
-                writeln!(&mut self.writer, "</thead>")?;
-            }
 
-            writeln!(&mut self.writer, "<tbody>")?;
-            for y in 0..output.body.n.y {
-                writeln!(&mut self.writer, "<tr>")?;
-                for x in output.body.iter_x(y) {
-                    let cell = output.body.get(CellPos { x, y });
-                    if cell.is_top_left() {
-                        let is_header = x < output.body.h[Axis2::X] || y < output.body.h[Axis2::Y];
-                        let tag = if is_header { "th" } else { "td" };
-                        self.put_cell(
-                            DrawCell::new(cell.inner(), &output.body),
-                            cell.rect(),
-                            tag,
-                            Some(&output.body),
-                        )?;
+                // Write table body inside <tbody>.
+                w.create_element("tbody").write_inner_content(|w| {
+                    for y in 0..output.body.n.y {
+                        w.create_element("tr").write_inner_content(|w| {
+                            for x in output.body.iter_x(y) {
+                                let cell = output.body.get(CellPos { x, y });
+                                if cell.is_top_left() {
+                                    let tag = if x < output.body.h[Axis2::X]
+                                        || y < output.body.h[Axis2::Y]
+                                    {
+                                        "th"
+                                    } else {
+                                        "td"
+                                    };
+                                    write_cell(
+                                        w,
+                                        DrawCell::new(cell.inner(), &output.body),
+                                        tag,
+                                        Some((cell.rect(), Some(&output.body))),
+                                    )?;
+                                }
+                            }
+                            Ok(())
+                        })?;
                     }
+                    Ok(())
+                })?;
+
+                if output.caption.is_some() || output.footnotes.is_some() {
+                    w.create_element("tfoot").write_inner_content(|w| {
+                        if let Some(caption) = output.caption {
+                            w.create_element("tr").write_inner_content(|w| {
+                                write_cell(
+                                    w,
+                                    DrawCell::new(
+                                        caption.get(CellPos::new(0, 0)).inner(),
+                                        &caption,
+                                    ),
+                                    "td",
+                                    Some((CellRect::new(0..output.body.n[Axis2::X], 0..1), None)),
+                                )
+                            })?;
+                        }
+                        if let Some(footnotes) = output.footnotes {
+                            for cell in footnotes.cells() {
+                                w.create_element("tr").write_inner_content(|w| {
+                                    write_cell(
+                                        w,
+                                        DrawCell::new(cell.inner(), &footnotes),
+                                        "td",
+                                        Some((
+                                            CellRect::new(0..output.body.n[Axis2::X], 0..1),
+                                            None,
+                                        )),
+                                    )?;
+                                    Ok(())
+                                })?;
+                            }
+                        }
+
+                        Ok(())
+                    })?;
                 }
-                writeln!(&mut self.writer, "</tr>")?;
+
+                Ok(())
+            })?;
+        Ok(())
+    }
+
+    fn write_table(&mut self, pivot_table: &PivotTable) -> std::io::Result<()> {
+        for layer_indexes in pivot_table.layers(true) {
+            let output = pivot_table.output(&layer_indexes, false);
+            self.write_layer(pivot_table, output)?;
+        }
+        Ok(())
+    }
+
+    fn write_graph(&mut self, graph: &Graph) -> std::io::Result<()> {
+        match graph.to_svg(None, |w| eprintln!("{w}")) {
+            Ok(svg) => {
+                let svg = svg.to_string();
+                self.writer
+                    .as_mut()
+                    .unwrap()
+                    .create_element("div")
+                    .write_inner_content(|w| {
+                        w.get_mut().write_all(svg.as_bytes())?;
+                        Ok(())
+                    })?;
+                Ok(())
             }
-            writeln!(&mut self.writer, "</tbody>")?;
-
-            if output.caption.is_some() || output.footnotes.is_some() {
-                writeln!(&mut self.writer, "<tfoot>")?;
-                writeln!(&mut self.writer, "<tr>")?;
-                if let Some(caption) = output.caption {
-                    self.put_cell(
-                        DrawCell::new(caption.get(CellPos::new(0, 0)).inner(), &caption),
-                        CellRect::new(0..output.body.n[Axis2::X], 0..1),
-                        "td",
-                        None,
-                    )?;
-                }
-                writeln!(&mut self.writer, "</tr>")?;
-
-                if let Some(footnotes) = output.footnotes {
-                    for cell in footnotes.cells() {
-                        writeln!(&mut self.writer, "<tr>")?;
-                        self.put_cell(
-                            DrawCell::new(cell.inner(), &footnotes),
-                            CellRect::new(0..output.body.n[Axis2::X], 0..1),
-                            "td",
-                            None,
-                        )?;
-                        writeln!(&mut self.writer, "</tr>")?;
-                    }
-                }
-                writeln!(&mut self.writer, "</tfoot>")?;
+            Err(error) => {
+                println!("Error rendering graph: {error}");
+                Ok(())
             }
         }
+    }
+
+    fn write_image(&mut self, image: &[u8]) -> std::io::Result<()> {
+        let data = Base64Display::new(image, &STANDARD);
+        let src = format!("data:image/png;base64,{data}");
+        self.writer
+            .as_mut()
+            .unwrap()
+            .create_element("img")
+            .with_attribute(("src", src.as_str()))
+            .write_empty()?;
         Ok(())
     }
 
-    fn put_cell(
-        &mut self,
-        cell: DrawCell<'_, '_>,
-        rect: CellRect,
-        tag: &str,
-        table: Option<&Table>,
-    ) -> std::io::Result<()> {
-        write!(&mut self.writer, "<{tag}")?;
-        let (body, suffixes) = cell.display().split();
-
-        let mut style = String::new();
-        let horz_align = match cell.horz_align(&body) {
-            HorzAlign::Right | HorzAlign::Decimal { .. } => Some("right"),
-            HorzAlign::Center => Some("center"),
-            HorzAlign::Left => None,
-        };
-        if let Some(horz_align) = horz_align {
-            write!(&mut style, "text-align: {horz_align}; ").unwrap();
+    fn write_item(&mut self, item: &Item) -> std::io::Result<()> {
+        match &item.details {
+            Details::Heading(_) => Ok(()),
+            Details::Graph(graph) => self.write_graph(graph),
+            Details::Image(image) => self.write_image(&image.0),
+            Details::Message(_diagnostic) => todo!(),
+            Details::Table(pivot_table) => self.write_table(pivot_table),
+            Details::Text(text) => {
+                if text.type_ != TextType::PageTitle {
+                    let value_options = ValueOptions::default();
+                    let cell = DrawCell::for_value(&text.content, &value_options);
+                    write_cell(self.writer.as_mut().unwrap(), cell, "p", None)?;
+                }
+                Ok(())
+            }
         }
+    }
+}
 
-        if cell.rotate {
-            write!(&mut style, "writing-mode: sideways-lr; ").unwrap();
-        }
+impl<W> Drop for HtmlDriver<W>
+where
+    W: Write,
+{
+    fn drop(&mut self) {
+        let _ = self.close();
+    }
+}
 
-        let vert_align = match cell.cell_style.vert_align {
-            VertAlign::Top => None,
-            VertAlign::Middle => Some("middle"),
-            VertAlign::Bottom => Some("bottom"),
-        };
-        if let Some(vert_align) = vert_align {
-            write!(&mut style, "vertical-align: {vert_align}; ").unwrap();
-        }
-        let bg = cell.font_style.bg;
-        if bg != Color::WHITE {
-            write!(&mut style, "background: {}; ", bg.display_css()).unwrap();
+fn write_newlines<W>(w: &mut Writer<W>, s: &str) -> std::io::Result<()>
+where
+    W: Write,
+{
+    for line in s.split_inclusive("\n") {
+        let (fragment, br) = line
+            .strip_suffix("\n")
+            .map_or((line, false), |rest| (rest, true));
+        w.write_event(Event::Text(BytesText::new(fragment)))?;
+        if br {
+            w.create_element("br").write_empty()?;
         }
+    }
+    Ok(())
+}
 
-        let fg = cell.font_style.fg;
-        if fg != Color::BLACK {
-            write!(&mut style, "color: {}; ", fg.display_css()).unwrap();
-        }
+fn write_cell<W>(
+    w: &mut Writer<W>,
+    cell: DrawCell<'_, '_>,
+    tag: &str,
+    rect_table: Option<(CellRect, Option<&Table>)>,
+) -> std::io::Result<()>
+where
+    W: Write,
+{
+    let (body, suffixes) = cell.display().split();
 
-        if !cell.font_style.font.is_empty() {
-            write!(
-                &mut style,
-                r#"font-family: "{}"; "#,
-                Escape::new(&cell.font_style.font)
-            )
-            .unwrap();
-        }
+    let mut style = String::new();
+    let horz_align = match cell.horz_align(&body) {
+        HorzAlign::Right | HorzAlign::Decimal { .. } => Some("right"),
+        HorzAlign::Center => Some("center"),
+        HorzAlign::Left => None,
+    };
 
-        if cell.font_style.bold {
-            write!(&mut style, "font-weight: bold; ").unwrap();
-        }
-        if cell.font_style.italic {
-            write!(&mut style, "font-style: italic; ").unwrap();
-        }
-        if cell.font_style.underline {
-            write!(&mut style, "text-decoration: underline; ").unwrap();
-        }
-        if cell.font_style.size != 0 {
-            write!(&mut style, "font-size: {}pt; ", cell.font_style.size).unwrap();
+    if let Some(horz_align) = horz_align {
+        write!(&mut style, "text-align: {horz_align}; ").unwrap();
+    }
+
+    if cell.rotate {
+        write!(&mut style, "writing-mode: sideways-lr; ").unwrap();
+    }
+
+    let vert_align = match cell.cell_style.vert_align {
+        VertAlign::Top => None,
+        VertAlign::Middle => Some("middle"),
+        VertAlign::Bottom => Some("bottom"),
+    };
+    if let Some(vert_align) = vert_align {
+        write!(&mut style, "vertical-align: {vert_align}; ").unwrap();
+    }
+    let bg = cell.font_style.bg;
+    if bg != Color::WHITE {
+        write!(&mut style, "background: {}; ", bg.display_css()).unwrap();
+    }
+
+    let fg = cell.font_style.fg;
+    if fg != Color::BLACK {
+        write!(&mut style, "color: {}; ", fg.display_css()).unwrap();
+    }
+
+    let font = cell.font_style.font.as_ref();
+    if !font.is_empty() {
+        write!(&mut style, r#"font-family: "{}""#, Escape::new(font)).unwrap();
+        if font.contains("Mono") || font.contains("mono") {
+            style.push_str(", monospace");
+        } else if font.contains("Sans") || font.contains("sans") {
+            style.push_str(", sans-serif");
+        } else if font.contains("serif") {
+            style.push_str(", serif");
         }
+        style.push_str("; ");
+    }
 
+    if cell.font_style.bold {
+        write!(&mut style, "font-weight: bold; ").unwrap();
+    }
+    if cell.font_style.italic {
+        write!(&mut style, "font-style: italic; ").unwrap();
+    }
+    if cell.font_style.underline {
+        write!(&mut style, "text-decoration: underline; ").unwrap();
+    }
+    if cell.font_style.size != 0 {
+        write!(&mut style, "font-size: {}pt; ", cell.font_style.size).unwrap();
+    }
+
+    let (col_span, row_span) = if let Some((rect, table)) = rect_table {
         if let Some(table) = table {
-            Self::put_border(&mut style, table.get_rule(Axis2::Y, rect.top_left()), "top");
-            Self::put_border(
+            fn put_border(dst: &mut String, style: Option<BorderStyle>, border_name: &str) {
+                if let Some(style) = style
+                    && let Some(css_style) = style.stroke.as_css()
+                {
+                    write!(dst, "border-{border_name}: {css_style}").unwrap();
+                    if style.color != Color::BLACK {
+                        write!(dst, " {}", style.color.display_css()).unwrap();
+                    }
+                    write!(dst, "; ").unwrap();
+                }
+            }
+
+            put_border(&mut style, table.get_rule(Axis2::Y, rect.top_left()), "top");
+            put_border(
                 &mut style,
                 table.get_rule(Axis2::X, rect.top_left()),
                 "left",
             );
             if rect.x.end == table.n[Axis2::X] {
-                Self::put_border(
+                put_border(
                     &mut style,
                     table.get_rule(Axis2::X, CellPos::new(rect.x.end, rect.y.start)),
                     "right",
                 );
             }
             if rect.y.end == table.n[Axis2::Y] {
-                Self::put_border(
+                put_border(
                     &mut style,
                     table.get_rule(Axis2::Y, CellPos::new(rect.x.start, rect.y.end)),
                     "bottom",
@@ -253,110 +422,59 @@ where
             }
         }
 
-        if !style.is_empty() {
-            write!(
-                &mut self.writer,
-                " style='{}'",
-                Escape::new(style.trim_end_matches("; "))
-                    .with_apos("&apos;")
-                    .with_quote("\"")
-            )?;
-        }
-
         let col_span = rect.x.len();
-        if col_span > 1 {
-            write!(&mut self.writer, r#" colspan="{col_span}""#)?;
-        }
+        let col_span = (col_span > 1).then(|| col_span.to_small_string::<8>());
 
         let row_span = rect.y.len();
-        if row_span > 1 {
-            write!(&mut self.writer, r#" rowspan="{row_span}""#)?;
-        }
-
-        write!(&mut self.writer, ">")?;
-
-        let mut text = SmallString::<[u8; 64]>::new();
-        write!(&mut text, "{body}").unwrap();
-        write!(
-            &mut self.writer,
-            "{}",
-            Escape::new(&text).with_newline("<br>")
-        )?;
-
-        if suffixes.has_subscripts() {
-            write!(&mut self.writer, "<sub>")?;
-            for (index, subscript) in suffixes.subscripts().enumerate() {
-                if index > 0 {
-                    write!(&mut self.writer, ",")?;
-                }
-                write!(
-                    &mut self.writer,
-                    "{}",
-                    Escape::new(subscript)
-                        .with_space("&nbsp;")
-                        .with_newline("<br>")
-                )?;
-            }
-            write!(&mut self.writer, "</sub>")?;
-        }
-
-        if suffixes.has_footnotes() {
-            write!(&mut self.writer, "<sup>")?;
-            for (index, footnote) in suffixes.footnotes().enumerate() {
-                if index > 0 {
-                    write!(&mut self.writer, ",")?;
-                }
-                let mut marker = SmallString::<[u8; 8]>::new();
-                write!(&mut marker, "{footnote}").unwrap();
-                write!(
-                    &mut self.writer,
-                    "{}",
-                    Escape::new(&marker)
-                        .with_space("&nbsp;")
-                        .with_newline("<br>")
-                )?;
+        let row_span = (row_span > 1).then(|| row_span.to_small_string::<8>());
+
+        (col_span, row_span)
+    } else {
+        (None, None)
+    };
+
+    w.create_element(tag)
+        .with_attributes(
+            style
+                .is_empty()
+                .not()
+                .then_some(("style", style.trim_end_matches("; "))),
+        )
+        .with_attributes(col_span.as_deref().map(|colspan| ("colspan", colspan)))
+        .with_attributes(row_span.as_deref().map(|rowspan| ("rowspan", rowspan)))
+        .write_inner_content(|w| {
+            let body = body.to_small_string::<64>();
+            write_newlines(w, &body)?;
+
+            if suffixes.has_subscripts() {
+                w.create_element("sub").write_inner_content(|w| {
+                    for (index, subscript) in suffixes.subscripts().enumerate() {
+                        if index > 0 {
+                            w.write_event(Event::Text(BytesText::new(",")))?;
+                        }
+                        write_newlines(w, subscript)?;
+                    }
+                    Ok(())
+                })?;
             }
-            write!(&mut self.writer, "</sup>")?;
-        }
-
-        writeln!(&mut self.writer, "</{tag}>")
-    }
-
-    fn put_border(dst: &mut String, style: Option<BorderStyle>, border_name: &str) {
-        if let Some(style) = style
-            && let Some(css_style) = style.stroke.as_css()
-        {
-            write!(dst, "border-{border_name}: {css_style}").unwrap();
-            if style.color != Color::BLACK {
-                write!(dst, " {}", style.color.display_css()).unwrap();
+            if suffixes.has_footnotes() {
+                w.create_element("sup").write_inner_content(|w| {
+                    for (index, footnote) in suffixes.footnotes().enumerate() {
+                        if index > 0 {
+                            w.write_event(Event::Text(BytesText::new(",")))?;
+                        }
+                        let marker = footnote.to_small_string::<8>();
+                        write_newlines(w, marker.as_str())?;
+                    }
+                    Ok(())
+                })?;
             }
-            write!(dst, "; ").unwrap();
-        }
-    }
-}
-
-fn put_header<W>(mut writer: W) -> std::io::Result<()>
-where
-    W: Write,
-{
-    write!(
-        &mut writer,
-        r#"<!doctype html>
-<html>
-<head>
-<title>PSPP Output</title>
-<meta name="generator" content="PSPP {}"/>
-<meta http-equiv="content-type" content="text/html; charset=utf8"/>
-{}
-"#,
-        Escape::new(env!("CARGO_PKG_VERSION")),
-        HEADER_CSS,
-    )?;
+            Ok(())
+        })?;
     Ok(())
 }
 
-const HEADER_CSS: &str = r#"<style>
-body {
+const CSS: &str = r#"body {
   background: white;
   color: black;
   padding: 0em 12em 0em 3em;
@@ -400,8 +518,7 @@ table {
   margin-bottom: 1em
 }
 caption {
-  text-align: left;
-  width: 100%
+  text-align: left
 }
 th { font-weight: normal }
 a:link {
@@ -413,9 +530,6 @@ a:visited {
 a:active {
   color: red;
 }
-</style>
-</head>
-<body>
 "#;
 
 impl<W> Driver for HtmlDriver<W>
@@ -427,13 +541,10 @@ where
     }
 
     fn write(&mut self, item: &Arc<Item>) {
-        match &item.details {
-            Details::Graph | Details::Image(_) | Details::Heading(_) => todo!(),
-            Details::Message(_diagnostic) => todo!(),
-            Details::Table(pivot_table) => {
-                self.render(pivot_table).unwrap(); // XXX
-            }
-            Details::Text(_text) => todo!(),
+        let mut cursor = item.clone().cursor();
+        while let Some(item) = cursor.cur() {
+            self.write_item(&item).unwrap(); // XXX
+            cursor.next();
         }
     }
 }
index d121a9a99d536c6f99d38259c525f76fc4ebced2..570630e3f38e7d67b10b4bb072392af7275319e1 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 723d0e2dabfed7f00ebf430c2c5343fd27390eb1..b1bfedb21ddea405008b47a81400541d14c01f79 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -61,7 +61,7 @@ impl Driver for PorDriver {
         dictionary: &'a Dictionary,
     ) -> anyhow::Result<Option<Box<dyn CaseWriter + 'a>>> {
         Ok(Some(Box::new(PorDriverCaseWriter {
-            writer: WriteOptions::new().write_file(&dictionary, &self.file)?,
+            writer: WriteOptions::new().write_file(dictionary, &self.file)?,
         })))
     }
 }
index 75e1c6162a6b9e6366e437da4c3d86d053f3fbdc..abb321881f5dc1da4547c7b55188ca35ad452058 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -66,7 +66,7 @@ impl Driver for SavDriver {
         Ok(Some(Box::new(SavDriverCaseWriter {
             writer: WriteOptions::new()
                 .with_compression(self.compression)
-                .write_file(&dictionary, &self.file)?,
+                .write_file(dictionary, &self.file)?,
         })))
     }
 }
index 22dde372356eb89969c7e7a7458d9729d085fd0a..4759fb53e64fa31d92b6bca3d89fe5a94383d864 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -65,10 +65,6 @@ where
     fn handles_show(&self) -> bool {
         true
     }
-
-    fn handles_groups(&self) -> bool {
-        true
-    }
 }
 
 impl SpvDriver<File> {
index 5b778246ca581aa1b38c059e82cdb026d17660a7..6d6fa034cb125b1f6af66c1721aac6b4526a0b18 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 
 use std::{
     borrow::Cow,
+    ffi::{OsStr, OsString},
     fmt::{Display, Error as FmtError, Result as FmtResult, Write as FmtWrite},
     fs::File,
-    io::{BufWriter, Write as IoWrite, stdout},
+    io::{BufWriter, Cursor, Write as IoWrite, stdout},
+    iter::chain,
     ops::{Index, Range},
-    path::PathBuf,
+    path::{Path, PathBuf},
     sync::{Arc, LazyLock},
 };
 
+use cairo::{Context, Format::ARgb32, ImageSurface, Rectangle};
 use enum_map::{Enum, EnumMap, enum_map};
+use gio::ReadInputStream;
+use itertools::Itertools;
+use num::NumCast;
+use rsvg::{CairoRenderer, Loader};
 use serde::{Deserialize, Serialize};
 use unicode_linebreak::{BreakOpportunity, linebreaks};
 use unicode_width::UnicodeWidthStr;
 
 use crate::output::{
-    Itemlike, drivers::text::text_line::Attribute, pivot::look::Color, render::Extreme,
+    Graph, Itemlike, drivers::text::text_line::Attribute, pivot::look::Color, render::Extreme,
     table::DrawCell,
 };
 
@@ -98,6 +105,14 @@ pub struct TextRendererOptions {
 
     /// ASCII or Unicode
     pub boxes: Boxes,
+
+    /// Template filename for image and chart file names.  The name should
+    /// contain one `#`, which is replaced by a sequential number.
+    /// `Some("none")` explicitly disables this feature.
+    ///
+    /// Image output is normally in PNG format but if the template ends in
+    /// `.svg` then it will be in SVG instead.
+    pub images: Option<PathBuf>,
 }
 
 pub struct TextRenderer {
@@ -110,6 +125,13 @@ pub struct TextRenderer {
     /// Minimum cell size to break across pages.
     min_hbreak: usize,
 
+    /// Template filename for image and chart file names.  A `#` in the name is
+    /// replaced by a sequential number.
+    image_template: Option<PathBuf>,
+
+    /// Number of image files written already.
+    n_images: usize,
+
     box_chars: &'static BoxChars,
 
     params: Params,
@@ -129,6 +151,10 @@ impl TextRenderer {
     }
 
     pub fn new(config: &TextRendererOptions) -> Self {
+        Self::new_with_base_path(config, None)
+    }
+
+    pub fn new_with_base_path(config: &TextRendererOptions, base_path: Option<&Path>) -> Self {
         let width = config.width.unwrap_or(usize::MAX).min(isize::MAX as usize) as isize;
         Self {
             emphasis: config.emphasis,
@@ -136,6 +162,24 @@ impl TextRenderer {
             min_hbreak: 20,
             box_chars: config.boxes.box_chars(),
             n_objects: 0,
+            image_template: if let Some(template) = &config.images {
+                if template != "none" {
+                    Some(template.clone())
+                } else {
+                    None
+                }
+            } else if let Some(base_path) = base_path {
+                base_path.with_extension("").file_name().map(|file_name| {
+                    base_path.with_file_name(
+                        [file_name, OsStr::new("-#.png")]
+                            .into_iter()
+                            .collect::<OsString>(),
+                    )
+                })
+            } else {
+                None
+            },
+            n_images: 0,
             params: Params {
                 size: Coord2::new(width, isize::MAX),
                 font_size: EnumMap::from_fn(|_| 1),
@@ -398,7 +442,7 @@ impl TextDriver {
                 Some(file) => Box::new(BufWriter::new(File::create(file)?)),
                 None => Box::new(stdout()),
             },
-            renderer: TextRenderer::new(&config.options),
+            renderer: TextRenderer::new_with_base_path(&config.options, config.file.as_deref()),
         })
     }
 }
@@ -420,15 +464,12 @@ impl TextRenderer {
         W: FmtWrite,
     {
         for item in item
-            .iter_in_order()
+            .iter_preorder()
             .without_hidden()
             .filter(|item| !item.details.is_heading())
         {
             match &item.details {
-                Details::Graph => {
-                    self.start_object(writer)?;
-                    writeln!(writer, "Omitting chart from text output")?
-                }
+                Details::Graph(graph) => self.render_graph(item, graph, writer)?,
                 Details::Image(_) => {
                     self.start_object(writer)?;
                     writeln!(writer, "Omitting image from text output")?
@@ -444,6 +485,79 @@ impl TextRenderer {
         Ok(())
     }
 
+    fn render_graph<W>(&mut self, item: &Item, graph: &Graph, writer: &mut W) -> FmtResult
+    where
+        W: FmtWrite,
+    {
+        self.start_object(writer)?;
+        if let Some(label) = &item.label {
+            write!(writer, "{label}: ")?;
+        }
+        let Some(template) = &self.image_template else {
+            return writeln!(writer, "Omitting graph from text output.");
+        };
+        self.n_images += 1;
+        let template_bytes = template.as_os_str().as_encoded_bytes();
+        let file_name = if let Some(hash) = template_bytes.iter().rposition(|x| *x == b'#') {
+            let n_images = self.n_images.to_string();
+            let file_name = chain(&template_bytes[..hash], n_images.as_bytes())
+                .chain(&template_bytes[hash + 1..])
+                .copied()
+                .collect_vec();
+
+            // SAFETY: This string consists of content that came from
+            // OsStr::as_encoded_bytes, split on an ASCII character, plus
+            // content that is ASCII characters.
+            PathBuf::from(unsafe { OsString::from_encoded_bytes_unchecked(file_name) })
+        } else {
+            template.clone()
+        };
+
+        let spv_info = item.spv_info();
+        let members = if let Some(spv_info) = &spv_info
+            && let Some(members) = &spv_info.members
+        {
+            members.graph_members()
+        } else {
+            None
+        };
+        match graph.to_svg(members, |w| eprintln!("{w}")) {
+            Ok(svg) => {
+                let svg = svg.to_string();
+                if file_name.extension() == Some(OsStr::new("svg")) {
+                    std::fs::write(&file_name, &svg).unwrap();
+                } else {
+                    let loader = Loader::new();
+                    let stream = ReadInputStream::new(Cursor::new(svg));
+                    let svg = loader
+                        .read_stream(&stream, gio::File::NONE, gio::Cancellable::NONE)
+                        .unwrap();
+                    let renderer = CairoRenderer::new(&svg);
+                    let (width, height) = renderer
+                        .intrinsic_size_in_pixels()
+                        .unwrap_or((400.0, 300.0));
+                    dbg!(width, height);
+                    let width = NumCast::from(width.ceil()).unwrap_or(400);
+                    let height = NumCast::from(height.ceil()).unwrap_or(300);
+
+                    let surface = ImageSurface::create(ARgb32, width, height).unwrap();
+                    let context = Context::new(&surface).unwrap();
+                    renderer
+                        .render_document(
+                            &context,
+                            &Rectangle::new(0.0, 0.0, width as f64, height as f64),
+                        )
+                        .unwrap();
+                    surface
+                        .write_to_png(&mut File::create(&file_name).unwrap())
+                        .unwrap();
+                }
+                writeln!(writer, "Graph written to {}.", file_name.display())
+            }
+            Err(error) => writeln!(writer, "Error rendering graph: {error}"),
+        }
+    }
+
     fn render_table<W>(&mut self, table: &PivotTable, writer: &mut W) -> FmtResult
     where
         W: FmtWrite,
@@ -452,7 +566,7 @@ impl TextRenderer {
             self.start_object(writer)?;
 
             let mut pager = Pager::new(self, table, Some(layer_indexes.as_slice()));
-            while pager.has_next(self).is_some() {
+            while pager.get_next(self).is_some() {
                 pager.draw_next(self, isize::MAX);
                 match self.emphasis {
                     Some(Emphasis::Ansi) => {
@@ -733,7 +847,7 @@ impl Device for TextRenderer {
                 continue;
             };
 
-            self.get_line(y as usize).put(x as usize, &text, attribute);
+            self.get_line(y as usize).put(x as usize, text, attribute);
         }
     }
 
index f93dda5d8ed5e6dac0befc5feedca826678e4aeb..b51f58d927e64284ca2d40918008a2168b335784 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 3104f8ba16a7e78f98d181239241790b47bf7a78..cca29d171d82f64978083073a209b7688a230bad 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -79,7 +79,7 @@ pub struct PageSetup {
     pub footer: Document,
 }
 
-static CATALOG: LazyLock<Catalog> = LazyLock::new(|| Catalog::new());
+static CATALOG: LazyLock<Catalog> = LazyLock::new(Catalog::new);
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Margins(pub EnumMap<Axis2, [Length; 2]>);
index 08d774a1f5274665043bc43083614e97686ddeb3..c6649e56c70c74d876c937920c59c787ad73ba22 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -57,7 +57,7 @@ use chrono::{NaiveDateTime, Utc};
 use enum_iterator::Sequence;
 use enum_map::{Enum, EnumMap, enum_map};
 use itertools::Itertools;
-pub use look_xml::{Length, TableProperties};
+pub use look_xml::{Length, ParseLengthError, TableProperties};
 use serde::{Deserialize, Serialize, ser::SerializeMap};
 use smallvec::{SmallVec, smallvec};
 
@@ -634,6 +634,7 @@ pub struct Dimension {
 pub type GroupVec<'a> = SmallVec<[&'a Group; 4]>;
 
 /// A path from the root of a [Dimension] to a [Leaf].
+#[derive(Debug)]
 pub struct Path<'a> {
     /// Groups along the path.
     ///
@@ -913,7 +914,7 @@ impl Group {
     /// designates `self`.
     pub fn category(&self, locator: CategoryLocator) -> Option<&Category> {
         let path = self.locator_path(locator)?;
-        let mut this = &self.children[*path.get(0)?];
+        let mut this = &self.children[*path.first()?];
         for index in path[1..].iter().copied() {
             this = &this.as_group().unwrap().children[index];
         }
@@ -926,7 +927,7 @@ impl Group {
     /// designates `self`.
     pub fn category_mut(&mut self, locator: CategoryLocator) -> Option<&mut Category> {
         let path = self.locator_path(locator)?;
-        let mut this = &mut self.children[*path.get(0)?];
+        let mut this = &mut self.children[*path.first()?];
         for index in path[1..].iter().copied() {
             this = &mut this.as_group_mut().unwrap().children[index];
         }
@@ -1277,7 +1278,7 @@ impl From<&String> for Category {
 }
 
 /// An axis of a 2-dimensional table.
-#[derive(Copy, Clone, Debug, Enum, PartialEq, Eq, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Enum, PartialEq, Eq, Serialize, Deserialize, Sequence)]
 #[serde(rename_all = "snake_case")]
 pub enum Axis2 {
     /// X axis.
index 98ad48fe315035f00d68504858076fd7ab62f0ac..c41656f273f5047907bd2078fd1516c85f6bedc6 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -27,6 +27,7 @@
 #![warn(dead_code)]
 
 use std::{
+    borrow::Cow,
     fmt::{Debug, Display},
     io::Read,
     ops::{Range, RangeInclusive},
@@ -36,7 +37,7 @@ use std::{
 
 use color::{AlphaColor, Rgba8, Srgb, palette::css::TRANSPARENT};
 use enum_iterator::Sequence;
-use enum_map::{Enum, EnumMap, enum_map};
+use enum_map::{Enum, EnumMap};
 use quick_xml::{DeError, de::from_str};
 use serde::{Deserialize, Serialize, de::Visitor, ser::SerializeStruct};
 
@@ -631,7 +632,7 @@ pub struct AreaStyle {
 
 impl AreaStyle {
     /// Returns the default style for `area`.
-    pub fn default_for_area(area: Area) -> Self {
+    pub const fn default_for_area(area: Area) -> Self {
         Self {
             cell_style: CellStyle::default_for_area(area),
             font_style: FontStyle::default_for_area(area),
@@ -673,7 +674,7 @@ impl Default for CellStyle {
 
 impl CellStyle {
     /// Returns the default cell style for `area`.
-    pub fn default_for_area(area: Area) -> Self {
+    pub const fn default_for_area(area: Area) -> Self {
         use HorzAlign::*;
         use VertAlign::*;
         let (horz_align, vert_align, hmargins, vmargins) = match area {
@@ -689,7 +690,7 @@ impl CellStyle {
         Self {
             horz_align,
             vert_align,
-            margins: enum_map! { Axis2::X => hmargins, Axis2::Y => vmargins },
+            margins: EnumMap::from_array([hmargins, vmargins]),
         }
     }
     /// Returns the cell style with `horz_align` set as specified.
@@ -804,7 +805,7 @@ pub struct FontStyle {
     pub underline: bool,
 
     /// Typeface.
-    pub font: String,
+    pub font: Cow<'static, str>,
 
     /// Foreground color.
     pub fg: Color,
@@ -818,58 +819,70 @@ pub struct FontStyle {
 
 impl Default for FontStyle {
     fn default() -> Self {
+        Self::const_default()
+    }
+}
+
+impl FontStyle {
+    /// `const` version of [FontStyle::default].
+    pub const fn const_default() -> Self {
         FontStyle {
             bold: false,
             italic: false,
             underline: false,
-            font: String::from("Sans Serif"),
+            font: Cow::Borrowed("Sans Serif"),
             fg: Color::BLACK,
             bg: Color::WHITE,
             size: 9,
         }
     }
-}
 
-impl FontStyle {
     /// Returns the default font style for `area`.
-    pub fn default_for_area(area: Area) -> Self {
-        Self::default().with_bold(area == Area::Title)
+    pub const fn default_for_area(area: Area) -> Self {
+        Self::const_default().with_bold(matches!(area, Area::Title))
     }
+
     /// Returns the font with its `size` set as specified.
-    pub fn with_size(self, size: i32) -> Self {
-        Self { size, ..self }
+    pub const fn with_size(mut self, size: i32) -> Self {
+        self.size = size;
+        self
     }
     /// Returns the font with `bold` set as specified.
-    pub fn with_bold(self, bold: bool) -> Self {
-        Self { bold, ..self }
+    pub const fn with_bold(mut self, bold: bool) -> Self {
+        self.bold = bold;
+        self
     }
     /// Returns the font with `italic` set as specified.
-    pub fn with_italic(self, italic: bool) -> Self {
-        Self { italic, ..self }
+    pub const fn with_italic(mut self, italic: bool) -> Self {
+        self.italic = italic;
+        self
     }
     /// Returns the font with `underline` set as specified.
-    pub fn with_underline(self, underline: bool) -> Self {
-        Self { underline, ..self }
+    pub const fn with_underline(mut self, underline: bool) -> Self {
+        self.underline = underline;
+        self
     }
     /// Returns the font with `font` set as specified.
-    pub fn with_font(self, font: impl Into<String>) -> Self {
+    pub fn with_font(self, font: impl Into<Cow<'static, str>>) -> Self {
         Self {
             font: font.into(),
             ..self
         }
     }
     /// Returns the font with `fg` set as specified.
-    pub fn with_fg(self, fg: Color) -> Self {
-        Self { fg, ..self }
+    pub const fn with_fg(mut self, fg: Color) -> Self {
+        self.fg = fg;
+        self
     }
     /// Returns the font with `bg` set as specified.
-    pub fn with_bg(self, fg: Color) -> Self {
-        Self { fg, ..self }
+    pub const fn with_bg(mut self, bg: Color) -> Self {
+        self.bg = bg;
+        self
     }
 }
 
 /// Color used in [FontStyle].
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Color {
     /// Alpha channel.
     ///
@@ -930,11 +943,22 @@ impl Color {
         ColorDisplayCss(*self)
     }
 
+    /// Displays opaque colors as `rrggbb` and others as `rrggbbaa`.
+    pub fn display_hex(&self) -> impl Display {
+        ColorDisplayHex(*self)
+    }
+
     /// Returns the red, green, and blue channels of this color.
     pub fn into_rgb(&self) -> (u8, u8, u8) {
         (self.r, self.g, self.b)
     }
 
+    /// Returns the red, green, blue, and alpha channels of this color, in that
+    /// order.
+    pub fn into_rgba(&self) -> (u8, u8, u8, u8) {
+        (self.r, self.g, self.b, self.alpha)
+    }
+
     /// Returns 16-bit versions of the red, green, and blue channels of this
     /// color.
     pub fn into_rgb16(&self) -> (u16, u16, u16) {
@@ -1017,7 +1041,7 @@ struct ColorDisplayCss(Color);
 
 impl Display for ColorDisplayCss {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        let Color { alpha, r, g, b } = self.0;
+        let (r, g, b, alpha) = self.0.into_rgba();
         match alpha {
             255 => write!(f, "#{r:02x}{g:02x}{b:02x}"),
             _ => write!(f, "rgb({r}, {g}, {b}, {:.2})", alpha as f64 / 255.0),
@@ -1025,6 +1049,22 @@ impl Display for ColorDisplayCss {
     }
 }
 
+/// A structure for formatting a [Color] as hex digits.
+///
+/// See [Color::display_hex].
+struct ColorDisplayHex(Color);
+
+impl Display for ColorDisplayHex {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let (r, g, b, a) = self.0.into_rgba();
+        write!(f, "{r:02x}{g:02x}{b:02x}")?;
+        if a != 255 {
+            write!(f, "{a:02x}")?;
+        }
+        Ok(())
+    }
+}
+
 /// Style for drawing a border in a pivot table.
 #[derive(Copy, Clone, Debug, PartialEq, Deserialize)]
 pub struct BorderStyle {
index 58378a46df4b95d471d675f77e7ae0bdbe3c075c..b0787de1359bcffc8ff8bf3e00b6de30c07b88b8 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{fmt::Debug, num::ParseFloatError, str::FromStr};
+use std::{borrow::Cow, fmt::Debug, num::ParseFloatError, str::FromStr};
 
 use enum_map::{EnumMap, enum_map};
 use serde::{Deserialize, de::Visitor};
@@ -237,7 +237,7 @@ impl CellStyle {
                 bold,
                 italic: false,
                 underline: false,
-                font: String::from("SansSerif"),
+                font: Cow::from("SansSerif"),
                 fg,
                 bg,
                 size,
@@ -291,7 +291,7 @@ impl CellStyle {
             style.font_style.underline = font_underline == FontUnderline::Underline;
         }
         if let Some(font_family) = &self.font_family {
-            style.font_style.font = font_family.clone();
+            style.font_style.font = Cow::from(font_family.clone());
         }
 
         let (fg, bg) = if area.row_parity() == Some(RowParity::Odd)
@@ -566,10 +566,13 @@ impl Debug for Length {
 }
 
 impl FromStr for Length {
-    type Err = LengthParseError;
+    type Err = ParseLengthError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let s = s.trim_start();
+        if s.is_empty() {
+            return Err(ParseLengthError::EmptyLength);
+        }
         let unit = s.trim_start_matches(|c: char| c.is_ascii_digit() || c == '.' || c == ',');
         let value = &s[..s.len() - unit.len()];
         let number: f64 = if value.contains(',') {
@@ -577,28 +580,27 @@ impl FromStr for Length {
         } else {
             value.parse()
         }
-        .map_err(LengthParseError::ParseFloatError)?;
+        .map_err(ParseLengthError::ParseFloatError)?;
         let divisor = match unit.trim() {
-            // Inches.
             "in" | "인치" | "pol." | "cala" | "cali" => 1.0,
-
-            // Device-independent pixels.
             "px" => 96.0,
-
-            // Points.
             "pt" | "пт" | "" => 72.0,
-
-            // Centimeters.
             "cm" | "см" => 2.54,
-
-            other => return Err(LengthParseError::InvalidUnit(other.into())),
+            "mm" => 25.4,
+            "pc" => 12.0,
+            other => return Err(ParseLengthError::InvalidUnit(other.into())),
         };
         Ok(Length(number / divisor))
     }
 }
 
+/// Error parsing a [Length].
 #[derive(ThisError, Debug, PartialEq, Eq)]
-pub enum LengthParseError {
+pub enum ParseLengthError {
+    /// Empty length.
+    #[error("Length cannot be empty.")]
+    EmptyLength,
+
     /// Invalid number.
     #[error(transparent)]
     ParseFloatError(ParseFloatError),
@@ -636,7 +638,7 @@ impl<'de> Deserialize<'de> for Length {
 
 #[cfg(test)]
 mod tests {
-    use std::str::FromStr;
+    use std::{borrow::Cow, str::FromStr};
 
     use enum_map::{EnumMap, enum_map};
     use quick_xml::de::from_str;
@@ -648,7 +650,7 @@ mod tests {
             HeadingRegion, HorzAlign, LabelPosition, Look, RowColBorder, RowParity, Stroke,
             VertAlign,
         },
-        look_xml::{Length, LengthParseError, TableProperties},
+        look_xml::{Length, ParseLengthError, TableProperties},
     };
 
     #[test]
@@ -669,21 +671,16 @@ mod tests {
 
         assert_eq!(Length::from_str("2.54cm"), Ok(Length(1.0)));
 
-        assert_eq!(
-            Length::from_str(""),
-            Err(LengthParseError::ParseFloatError(
-                "".parse::<f64>().unwrap_err()
-            ))
-        );
+        assert_eq!(Length::from_str(""), Err(ParseLengthError::EmptyLength));
         assert_eq!(
             Length::from_str("1.2.3"),
-            Err(LengthParseError::ParseFloatError(
+            Err(ParseLengthError::ParseFloatError(
                 "1.2.3".parse::<f64>().unwrap_err()
             ))
         );
         assert_eq!(
             Length::from_str("1asdf"),
-            Err(LengthParseError::InvalidUnit("asdf".into()))
+            Err(ParseLengthError::InvalidUnit("asdf".into()))
         );
     }
 
@@ -779,7 +776,7 @@ mod tests {
                         bold: true,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -806,7 +803,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -833,7 +830,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -860,7 +857,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -889,7 +886,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -918,7 +915,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -945,7 +942,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -972,7 +969,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
@@ -999,7 +996,7 @@ mod tests {
                         bold: false,
                         italic: false,
                         underline: false,
-                        font: String::from("Sans Serif"),
+                        font: Cow::from("Sans Serif"),
                         fg: Color::BLACK,
                         bg: Color::WHITE,
                         size: 9,
index 8b295a4abf08f75a7fe2a80d19d597ce948bcab1..3559f6a9adc8d7f3c28b7a71393d6f588fa59ba4 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -468,19 +468,21 @@ impl<'a> Heading<'a> {
             return None;
         }
 
-        let mut columns = Vec::new();
-        let mut height = 0;
-        for indexes in column_enumeration.iter() {
-            let mut path = dimension.leaf_path(indexes[dim_index]).unwrap();
-            path.groups.retain(|group| group.show_label);
-            height = height.max(1 + path.groups.len());
-            columns.push(path);
-        }
+        let columns = column_enumeration
+            .iter()
+            .map(|indexes| {
+                let mut path = dimension.leaf_path(indexes[dim_index]).unwrap();
+                path.groups.retain(|group| group.show_label);
+                path
+            })
+            .collect::<Vec<_>>();
+
+        let height = columns.iter().map(|path| path.groups.len()).max()? + 1;
 
         Some(Self {
             dimension,
-            height,
             columns,
+            height,
         })
     }
 
index a7fee6a2f218c11d1b99ec2039e6655bf467ddc1..e772191da67836f524487a36fac076479fd125d5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -17,6 +17,7 @@
 use std::{fmt::Display, fs::File, path::Path, sync::Arc};
 
 use enum_map::EnumMap;
+use similar::TextDiff;
 
 use crate::{
     format::{DOLLAR40_2, F40_2, Format, PCT40_1},
@@ -131,20 +132,14 @@ where
     A: Display,
 {
     if expected != actual {
-        eprintln!("Unexpected output:\n--- {expected_name}\n+++ {actual_name}");
-        for result in diff::lines(expected, &actual) {
-            let (prefix, line) = match result {
-                diff::Result::Left(line) => ('-', line),
-                diff::Result::Both(line, _) => (' ', line),
-                diff::Result::Right(line) => ('+', line),
-            };
-            let suffix = if line.trim_end().len() != line.len() {
-                "$"
-            } else {
-                ""
-            };
-            eprintln!("{prefix}{line}{suffix}");
-        }
+        eprintln!("Unexpected output:\n");
+        let diff = TextDiff::from_lines(expected, actual);
+        eprint!(
+            "{}",
+            diff.unified_diff()
+                .context_radius(5)
+                .header(&expected_name.to_string(), &actual_name.to_string())
+        );
         panic!();
     }
 }
@@ -154,7 +149,7 @@ pub fn assert_rendering(name: &str, pivot_table: &PivotTable) {
     let item = Arc::new(pivot_table.clone().into_item());
     if let Some(dir) = std::env::var_os("PSPP_TEST_HTML_DIR") {
         let writer = File::create(Path::new(&dir).join(name).with_extension("html")).unwrap();
-        HtmlDriver::for_writer(writer).write(&item);
+        HtmlDriver::for_writer(writer).unwrap().write(&item);
     }
 
     let item = Arc::new(pivot_table.clone().into_item());
index 28f497232cf079fe2f028ee6d24065c043e8c4c1..1af155fcb471c1349c4c65d49b0fd8a90050f3b7 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
 
-use std::{fmt::Debug, io::Cursor};
+use std::{borrow::Cow, fmt::Debug, io::Cursor};
 
 use crate::output::pivot::{
     Axis2, FootnoteMarkerPosition, FootnoteMarkerType,
@@ -366,7 +366,7 @@ impl look::AreaStyle {
                 bold: style.weight > 400,
                 italic: style.italic,
                 underline: style.underline,
-                font: style.font_name.string.clone(),
+                font: Cow::from(style.font_name.string.clone()),
                 fg: style.text_color,
                 bg,
                 size: -style.font_size * 3 / 4,
index 9ee505f1039e17c0867c8319a8dd75a16d0dbedb..cc36517933a21ec493ad09a71421cf221d3aebe5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -231,7 +231,7 @@ impl Value {
     pub fn with_source_variable(self, variable: &Variable) -> Self {
         let value_label = self
             .datum()
-            .and_then(|datum| variable.value_labels.get(&datum).map(String::from));
+            .and_then(|datum| variable.value_labels.get(datum).map(String::from));
         self.with_value_label(value_label)
             .with_format(variable.print_format)
             .with_variable_name(Some(variable.name.as_str().into()))
@@ -293,7 +293,7 @@ impl Value {
     }
 
     /// Returns this `Value` with the added `subscripts`.
-    pub fn with_subscripts<'a>(
+    pub fn with_subscripts(
         mut self,
         subscripts: impl IntoIterator<Item = impl Into<String>>,
     ) -> Self {
@@ -398,16 +398,14 @@ impl Value {
     pub fn font_style(&self) -> Option<&FontStyle> {
         self.styling
             .as_ref()
-            .map(|styling| styling.font_style.as_ref())
-            .flatten()
+            .and_then(|styling| styling.font_style.as_ref())
     }
 
     /// Returns this `Value`'s cell style, if it has one.
     pub fn cell_style(&self) -> Option<&CellStyle> {
         self.styling
             .as_ref()
-            .map(|styling| styling.cell_style.as_ref())
-            .flatten()
+            .and_then(|styling| styling.cell_style.as_ref())
     }
 
     /// Returns this `Value`'s subscripts.
@@ -479,7 +477,7 @@ pub struct DisplayValue<'a> {
 
 impl<'a> DisplayValue<'a> {
     /// Returns the subscripts to be displayed, as an iterator of `&str`.
-    pub fn subscripts(&self) -> impl Iterator<Item = &str> + ExactSizeIterator + Clone {
+    pub fn subscripts(&self) -> impl ExactSizeIterator<Item = &str> + Clone {
         self.subscripts.iter().map(String::as_str)
     }
 
@@ -1020,7 +1018,7 @@ impl TemplateValue {
                 while let Some(c) = iter.next() {
                     match c {
                         '\\' => {
-                            let c = iter.next().unwrap_or('\\') as char;
+                            let c = iter.next().unwrap_or('\\');
                             let c = if c == 'n' { '\n' } else { c };
                             write!(f, "{c}")?;
                         }
@@ -1187,14 +1185,13 @@ impl ValueInner {
 
     /// Returns the value label inside this value, if it has one.
     fn value_label(&self) -> Option<&str> {
-        self.as_datum_value()
-            .and_then(|d| d.value_label.as_ref().map(String::as_str))
+        self.as_datum_value().and_then(|d| d.value_label.as_deref())
     }
 
     /// Returns the variable label inside this value, if it has one.
     fn variable_label(&self) -> Option<&str> {
         self.as_variable_value()
-            .and_then(|d| d.variable_label.as_ref().map(String::as_str))
+            .and_then(|d| d.variable_label.as_deref())
     }
 
     /// Returns the [DatumValue] inside this value, if it is
index 69676343b24787dae674c6dbe65a4a5793348d1a..8e6e7fd8722e7e8bf4456cea83125730fd2aa70a 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -249,19 +249,19 @@ impl RenderedTable {
         let mut unspanned_columns = EnumMap::from_fn(|_| vec![0; n.x]);
         for cell in table.cells().filter(|cell| cell.col_span() == 1) {
             let mut w = device.measure_cell_width(&DrawCell::new(cell.inner(), &table));
-            if device.params().px_size.is_some() {
-                if let Some(region) = table.heading_region(cell.pos) {
-                    let wr = &heading_widths[region];
-                    if w[Min] < wr[Min] {
-                        w[Min] = wr[Min];
-                        if w[Min] > w[Max] {
-                            w[Max] = w[Min];
-                        }
-                    } else if w[Max] > wr[Max] {
-                        w[Max] = wr[Max];
-                        if w[Max] < w[Min] {
-                            w[Min] = w[Max];
-                        }
+            if device.params().px_size.is_some()
+                && let Some(region) = table.heading_region(cell.pos)
+            {
+                let wr = &heading_widths[region];
+                if w[Min] < wr[Min] {
+                    w[Min] = wr[Min];
+                    if w[Min] > w[Max] {
+                        w[Max] = w[Min];
+                    }
+                } else if w[Max] > wr[Max] {
+                    w[Max] = wr[Max];
+                    if w[Max] < w[Min] {
+                        w[Min] = w[Max];
                     }
                 }
             }
@@ -1081,8 +1081,18 @@ impl Pager {
         }
     }
 
+    pub fn has_next(&self) -> bool {
+        self.y_break
+            .as_ref()
+            .is_some_and(|y_break| y_break.has_next())
+            || self
+                .x_break
+                .as_ref()
+                .is_some_and(|x_break| x_break.has_next())
+    }
+
     /// True if there's content left to render.
-    pub fn has_next(&mut self, device: &dyn Device) -> Option<&mut Break> {
+    pub fn get_next(&mut self, device: &dyn Device) -> Option<&mut Break> {
         // If there's a nonempty y_break, return it.
         if let Some(y_break) = self.y_break.as_mut()
             && y_break.has_next()
@@ -1122,7 +1132,7 @@ impl Pager {
         }
 
         let mut ofs = Coord2::new(0, 0);
-        while let Some(y_break) = self.has_next(device) {
+        while let Some(y_break) = self.get_next(device) {
             let Some(page) = y_break.next(device, space - ofs[Y]).unwrap_or_default() else {
                 break;
             };
index 228c76d9a77ba2935410dff5823f84dd6d8c91f4..4055716d72c71cc3d4885800371c42024c0bae86 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -539,6 +539,23 @@ impl<'a, 'b> DrawCell<'a, 'b> {
         }
     }
 
+    pub fn for_value(value: &'a Value, value_options: &'a ValueOptions) -> Self {
+        pub static DEFAULT_CELL_STYLE: CellStyle =
+            CellStyle::default_for_area(Area::Data(RowParity::Even));
+        pub static DEFAULT_FONT_STYLE: FontStyle = FontStyle::const_default();
+
+        Self {
+            rotate: false,
+            inner: &value.inner,
+            cell_style: value.cell_style().unwrap_or(&DEFAULT_CELL_STYLE),
+            font_style: value.font_style().unwrap_or(&DEFAULT_FONT_STYLE),
+            subscripts: value.subscripts(),
+            footnotes: value.footnotes(),
+            value_options,
+            substitutions: &|_| None,
+        }
+    }
+
     pub fn display(&self) -> DisplayValue<'a> {
         self.inner
             .display(self.value_options)
index 823397fc7067577c4dce849980c7630f22fe9348..2037e93dcd53044138d38fc05e02445b60b50440 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -829,7 +829,7 @@ where
 
             fn decode_optional_string(s: &[u8]) -> Option<String> {
                 let s = WINDOWS_1252.decode(s).0;
-                let s = s.trim_matches(&[' ', '\0']);
+                let s = s.trim_matches([' ', '\0']);
                 if s.is_empty() { None } else { Some(s.into()) }
             }
             let metadata = Metadata {
diff --git a/rust/pspp/src/percentiles.rs b/rust/pspp/src/percentiles.rs
new file mode 100644 (file)
index 0000000..278004e
--- /dev/null
@@ -0,0 +1,305 @@
+/// Percentile algorithm.
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum Algorithm {
+    /// Weighted average at lower observation.
+    WeightedAverage,
+    /// Closest observation.
+    Round,
+    /// Empirical distribution function.
+    Empirical,
+    /// Weighted average at higher observation.
+    ///
+    /// The `FREQUENCIES` command uses this algorithm, and this is the default
+    /// algorithm for `EXAMINE`.
+    #[default]
+    HighAverage,
+    /// Empirical distribution function with averaging.
+    AverageEmpirical,
+}
+
+impl Algorithm {
+    fn weight_adjustment(self) -> f64 {
+        match self {
+            Algorithm::WeightedAverage
+            | Algorithm::Round
+            | Algorithm::Empirical
+            | Algorithm::AverageEmpirical => 0.0,
+            Algorithm::HighAverage => 1.0,
+        }
+    }
+    pub fn target_cumulative_weight(self, total_weight: f64, p: f64) -> f64 {
+        (total_weight + self.weight_adjustment()) * p.clamp(0.0, 1.0)
+    }
+
+    /// Calculates a percentile for this algorithm at  target cumulative weight `tcw`,
+    /// given that `(a, b)` brackets the observation with that cumulative weight:
+    ///
+    /// - `a` is the observation with the least cumulative weight whose
+    ///   cumulative weight is at least `tcw`.
+    ///
+    /// - `b` is the observation with cumulative weight just above `a`.
+    ///
+    /// Corner cases:
+    ///
+    /// - `a` and `b` are both `None` if there are no observations.
+    ///
+    /// - If `a` is the observation with the greatest cumulative weight, then
+    ///   `b` is `None`.
+    ///
+    /// - If `tcw` is less than the least cumulative weight among the
+    ///   observations, then `b` is the observation with the least cumulative
+    ///   weight and `a` is `None`.
+    pub fn percentile(
+        self,
+        tcw: f64,
+        (a, b): (Option<Observation>, Option<Observation>),
+    ) -> Option<f64> {
+        let (a, b) = match (a, b) {
+            (None, None) => return None,
+            (None, Some(x)) | (Some(x), None) => return Some(x.y),
+            (Some(a), Some(b)) => (a, b),
+        };
+
+        let g = (tcw - a.cc) / b.c;
+        let gs = tcw - a.cc;
+
+        Some(match self {
+            Algorithm::WeightedAverage | Algorithm::HighAverage => {
+                if gs >= 1.0 {
+                    b.y
+                } else if b.c >= 1.0 {
+                    (1.0 - gs) * a.y + gs * b.y
+                } else {
+                    (1.0 - g) * a.y + g * b.y
+                }
+            }
+            Algorithm::Round => {
+                let threshold = if b.c >= 1.0 { gs } else { g };
+                if threshold < 0.5 { a.y } else { b.y }
+            }
+            Algorithm::Empirical => {
+                if gs == 0.0 {
+                    a.y
+                } else {
+                    b.y
+                }
+            }
+            Algorithm::AverageEmpirical => {
+                if gs == 0.0 {
+                    a.y.midpoint(b.y)
+                } else {
+                    b.y
+                }
+            }
+        })
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct Observation {
+    /// Observed value.
+    y: f64,
+
+    /// Weight.
+    ///
+    /// Must be positive.
+    c: f64,
+
+    /// Sum of weights up to and including this observation.
+    cc: f64,
+}
+
+pub trait Percentile {
+    /// Calculates percentile `0 <= p <= 1` using `algorithm` on the data
+    /// represented by this object.
+    fn percentile(&self, algorithm: Algorithm, p: f64) -> Option<f64>;
+}
+
+trait CumulativeFrequency {
+    /// Observed value.
+    fn y(&self) -> f64;
+
+    /// Sum of weights up to and including this observation.
+    fn cc(&self) -> f64;
+}
+
+impl<T> Percentile for [T]
+where
+    T: CumulativeFrequency,
+{
+    /// Calculates percentile `0 <= p <= 1` using `algorithm` on the data
+    /// represented by this object.
+    ///
+    /// The frequencies in the slice must be in strictly increasing order of
+    /// y-value and of cumulative weight.
+    fn percentile(&self, algorithm: Algorithm, p: f64) -> Option<f64> {
+        fn observations_around<T>(
+            this: &[T],
+            cum_weight: f64,
+        ) -> (Option<Observation>, Option<Observation>)
+        where
+            T: CumulativeFrequency,
+        {
+            fn get<T>(this: &[T], k: usize) -> Option<Observation>
+            where
+                T: CumulativeFrequency,
+            {
+                let obs = this.get(k)?;
+                let y = obs.y();
+                let cc = obs.cc();
+                let c = if k == 0 { cc } else { cc - this[k - 1].cc() };
+                Some(Observation { y, c, cc })
+            }
+
+            let k = this.partition_point(|obs| obs.cc() <= cum_weight);
+            let a = k.checked_sub(1).and_then(|k| get(this, k));
+            let b = get(this, k);
+            (a, b)
+        }
+
+        let tcw = algorithm.target_cumulative_weight(self.last()?.cc(), p);
+        algorithm.percentile(tcw, observations_around(self, tcw))
+    }
+}
+
+impl<T> Percentile for Vec<T>
+where
+    T: CumulativeFrequency,
+{
+    /// Calculates percentile `0 <= p <= 1` using `algorithm` on the data
+    /// represented by this object.
+    ///
+    /// The frequencies in the slice must be in strictly increasing order of
+    /// y-value and of cumulative weight.
+    fn percentile(&self, algorithm: Algorithm, p: f64) -> Option<f64> {
+        self.as_slice().percentile(algorithm, p)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::percentiles::{Algorithm, CumulativeFrequency, Percentile as _};
+
+    struct Item {
+        y: f64,
+        cc: f64,
+    }
+    impl CumulativeFrequency for Item {
+        fn y(&self) -> f64 {
+            self.y
+        }
+
+        fn cc(&self) -> f64 {
+            self.cc
+        }
+    }
+
+    #[test]
+    fn zero_observations() {
+        let data = Vec::<Item>::new();
+        for p in (0..=20).map(|p| p as f64 / 20.0) {
+            assert_eq!(data.percentile(Algorithm::HighAverage, p), None);
+        }
+    }
+
+    #[test]
+    fn one_observation() {
+        let data = vec![Item { y: 5.0, cc: 1.0 }];
+        for p in (0..=20).map(|p| p as f64 / 20.0) {
+            assert_eq!(data.percentile(Algorithm::HighAverage, p), Some(5.0));
+        }
+    }
+
+    #[test]
+    fn two_observations() {
+        let data = vec![Item { y: 2.0, cc: 1.0 }, Item { y: 5.0, cc: 2.0 }];
+
+        for (p, expected) in [
+            (0.00, "2.00"),
+            (0.05, "2.00"),
+            (0.10, "2.00"),
+            (0.15, "2.00"),
+            (0.20, "2.00"),
+            (0.25, "2.00"),
+            (0.30, "2.00"),
+            (0.35, "2.15"),
+            (0.40, "2.60"),
+            (0.45, "3.05"),
+            (0.50, "3.50"),
+            (0.55, "3.95"),
+            (0.60, "4.40"),
+            (0.65, "4.85"),
+            (0.70, "5.00"),
+            (0.75, "5.00"),
+            (0.80, "5.00"),
+            (0.85, "5.00"),
+            (0.90, "5.00"),
+            (0.95, "5.00"),
+            (1.00, "5.00"),
+        ] {
+            let percentile = data.percentile(Algorithm::HighAverage, p).unwrap();
+            assert_eq!(format!("{:.2}", percentile), expected);
+        }
+    }
+
+    #[test]
+    fn three_observations() {
+        let data = vec![
+            Item { y: 2.0, cc: 1.0 },
+            Item { y: 5.0, cc: 2.0 },
+            Item { y: 8.0, cc: 3.0 },
+        ];
+
+        let algorithms = [
+            Algorithm::HighAverage,
+            Algorithm::WeightedAverage,
+            Algorithm::Round,
+            Algorithm::Empirical,
+            Algorithm::AverageEmpirical,
+        ];
+        /*
+               for p in (0..=20).map(|p| p as f64 / 20.0) {
+                   println!(
+                       "({p:.2}, [{}]),",
+                       algorithms
+                           .into_iter()
+                           .map(|algorithm| format!("\"{:.2}\"", data.percentile(algorithm, p).unwrap()))
+                           .join(", ")
+                   );
+               }
+        */
+
+        for (p, expected) in [
+            (0.00, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.05, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.10, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.15, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.20, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.25, ["2.00", "2.00", "2.00", "2.00", "2.00"]),
+            (0.30, ["2.60", "2.00", "2.00", "2.00", "2.00"]),
+            (0.35, ["3.20", "2.15", "2.00", "5.00", "5.00"]),
+            (0.40, ["3.80", "2.60", "2.00", "5.00", "5.00"]),
+            (0.45, ["4.40", "3.05", "2.00", "5.00", "5.00"]),
+            (0.50, ["5.00", "3.50", "5.00", "5.00", "5.00"]),
+            (0.55, ["5.60", "3.95", "5.00", "5.00", "5.00"]),
+            (0.60, ["6.20", "4.40", "5.00", "5.00", "5.00"]),
+            (0.65, ["6.80", "4.85", "5.00", "5.00", "5.00"]),
+            (0.70, ["7.40", "5.30", "5.00", "8.00", "8.00"]),
+            (0.75, ["8.00", "5.75", "5.00", "8.00", "8.00"]),
+            (0.80, ["8.00", "6.20", "5.00", "8.00", "8.00"]),
+            (0.85, ["8.00", "6.65", "8.00", "8.00", "8.00"]),
+            (0.90, ["8.00", "7.10", "8.00", "8.00", "8.00"]),
+            (0.95, ["8.00", "7.55", "8.00", "8.00", "8.00"]),
+            (1.00, ["8.00", "8.00", "8.00", "8.00", "8.00"]),
+        ] {
+            for (algorithm, expected) in algorithms.into_iter().zip(expected) {
+                let percentile = data.percentile(algorithm, p).unwrap();
+                assert_eq!(
+                    format!("{:.2}", percentile),
+                    expected,
+                    "{algorithm:?} percentile {p:.2?}"
+                );
+            }
+        }
+    }
+}
index 5488c5a39ac3a4d803497d6d2a8369fbc8af1dc1..66cd49ab4846444be46418692a10128e9013cdbd 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index ba7454acb14ee73e531b175052ad68c7312505a4..91e33719e619f0c9c9192e0ccc06d2cf6ae03ab5 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -90,7 +90,7 @@ where
     S: Serializer,
 {
     let mut seq = serializer.serialize_seq(Some(256))?;
-    for (index, c) in translations.into_iter().enumerate() {
+    for (index, c) in translations.iter().enumerate() {
         let windows_1252 = *c as char;
         let cp_437 = CP437_WINGDINGS.decode(*c);
         if windows_1252 == cp_437 {
@@ -395,6 +395,7 @@ impl TranslationTable {
         // all set to '0', marking them as untranslatable, and that would screw
         // up our actual translation of the real '0'.
         let mut translations = [0; 256];
+        #[allow(clippy::needless_range_loop)]
         for portable in 64..=255 {
             let c = character_set[portable] as usize;
             if translations[c] == 0 {
@@ -671,7 +672,7 @@ where
                 };
                 let width = match width {
                     0 => VarWidth::Numeric,
-                    width => VarWidth::String(width as u16),
+                    width => VarWidth::String(width),
                 };
 
                 let print = read_format(&mut reader, width, |new_spec, format_error| {
@@ -1092,7 +1093,7 @@ where
     R: Read,
 {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
-        for (i, c) in buf.into_iter().enumerate() {
+        for (i, c) in buf.iter_mut().enumerate() {
             if self.at_newline {
                 *c = b' ';
                 self.line_length += 1;
index c5150c96ab667b7c04d2e27578c8ed561b74fdbd..b20d0a77abbd3c56f61b13939be75935eef99bfb 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -448,7 +448,7 @@ where
     pub fn write_case_weight(&mut self) -> Result<(), Error> {
         if let Some(weight_index) = self.dictionary.weight_index() {
             self.writer.write_all(b"6")?;
-            write_string(&mut self.writer, &self.short_names[weight_index].as_bytes())?;
+            write_string(&mut self.writer, self.short_names[weight_index].as_bytes())?;
         }
         Ok(())
     }
@@ -795,7 +795,7 @@ fn should_round_up(trigs: &[u8]) -> bool {
                 true
             } else {
                 // Exactly half: round to even.
-                trigs[0] % 2 != 0
+                !trigs[0].is_multiple_of(2)
             }
         }
     }
index 164956fbdcae583a3960a75150675b19b8de5af1..e5ec49e477ba28d70cd543469946477bb1bd0ac7 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 21224a757d9b06f5a9025c6819333cc9c8bccdff..0d06be7acb089ed309e482219ca38dce5b184855 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 4c889ec3031ba0a4811c409c92ae235448fd24a3..5eaa3eddf6c7c97006a33d4485d997affcbf2416 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,7 @@
 //! SPSS 16 and later uses to represent the contents of its output editor.  See also
 //! [SPV file format documentation].
 //!
-//! Use [ReadOptions] to read an SPV file.  Use [Writer] to write an SPV file.
+//! Use [SpvFile] to read an SPV file.  Use [Writer] to write an SPV file.
 //!
 //! [SPV file format documentation]: https://pspp.benpfaff.org/manual/spv/index.html
 
index 6a8c2c58dde50a76ca527bdc3fc40af71b1711af..1434a9f7c0730fd8b8779bd91c3d17e3848f3fc6 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
 //
 // You should have received a copy of the GNU General Public License along with
 // this program.  If not, see <http://www.gnu.org/licenses/>.
-#![allow(dead_code)]
 use std::{
     fmt::Display,
     fs::File,
     io::{BufReader, Read, Seek},
+    mem::take,
     path::Path,
 };
 
@@ -26,11 +26,13 @@ use serde::Deserialize;
 use zip::{ZipArchive, result::ZipError};
 
 use crate::{
-    crypto::EncryptedReader,
-    output::{Item, page::PageSetup},
+    crypto::MaybeEncryptedFile,
+    file::ReadSeek,
+    output::{Criteria, Item, page::PageSetup},
     spv::{
-        legacy_bin::LegacyBinWarning,
+        legacy_bin::{DataSources, LegacyBin, LegacyBinWarning},
         read::{
+            graph::GraphWarning,
             legacy_xml::LegacyXmlWarning,
             light::LightWarning,
             structure::{OutlineItem, StructureMember},
@@ -39,6 +41,8 @@ use crate::{
 };
 
 mod css;
+#[allow(missing_docs)]
+pub mod graph;
 pub mod html;
 pub mod legacy_bin;
 #[allow(missing_docs)]
@@ -79,14 +83,13 @@ pub enum WarningDetails {
     /// {0}
     LegacyXmlWarning(LegacyXmlWarning),
 
+    /// {0}
+    GraphWarning(GraphWarning),
+
     /// Unknown page orientation {0:?}.
     UnknownOrientation(String),
 }
 
-/// Both [Read] and [Seek] together.
-pub trait ReadSeek: Read + Seek {}
-impl<T> ReadSeek for T where T: Read + Seek {}
-
 /// A ZIP archive that contains an SPV file.
 pub struct SpvArchive<R>(pub ZipArchive<R>);
 
@@ -99,19 +102,17 @@ impl SpvArchive<Box<dyn ReadSeek>> {
         Self::open_reader(File::open(path)?, password)
     }
 
-    /// Opens the provided Zip `archive`.
-    ///
-    /// Any password provided for reading the file is unused, because if one was
-    /// needed then it must have already been used to open the archive.
+    /// Opens `reader` as an SPV archive, using `password` as the password if
+    /// the file is encrypted.
     pub fn open_reader<R>(reader: R, password: Option<&str>) -> Result<Self, Error>
     where
         R: Read + Seek + 'static,
     {
-        let reader = if let Some(password) = password {
-            Box::new(EncryptedReader::open(reader, password)?)
-        } else {
-            Box::new(reader) as Box<dyn ReadSeek>
-        };
+        let reader = MaybeEncryptedFile::new(reader)
+            .map_err(|error| error.details)?
+            .with_password(password)
+            .open()
+            .map_err(|error| error.details)?;
         let mut archive = ZipArchive::new(reader).map_err(|error| match error {
             ZipError::InvalidArchive(_) => Error::NotSpv,
             other => other.into(),
@@ -138,6 +139,8 @@ where
     ///
     /// Most callers want to use [read](Self::read), instead, to read the full
     /// contents of the SPV file.
+    ///
+    /// Use this function to apply [Criteria] before reading the full contents.
     pub fn read_outline<F>(&mut self, mut warn: F) -> Result<SpvOutline, Error>
     where
         F: FnMut(Warning),
@@ -162,7 +165,23 @@ where
     where
         F: FnMut(Warning),
     {
-        self.read_outline(&mut warn)?.read_items(self, &mut warn)
+        self.read_outline(&mut warn)?.read(self, &mut warn)
+    }
+
+    /// Reads the contents of the member with the given `name`.
+    pub fn read_member(&mut self, name: &str) -> Result<Vec<u8>, Error> {
+        let mut data = Vec::new();
+        self.0.by_name(name)?.read_to_end(&mut data)?;
+        Ok(data)
+    }
+
+    /// Reads `name` in the archive as a [LegacyBin] and decodes it.
+    pub fn read_legacy_bin(
+        &mut self,
+        name: &str,
+        warn: &mut dyn FnMut(LegacyBinWarning),
+    ) -> Result<DataSources, Error> {
+        LegacyBin::from_data(&self.read_member(name)?, warn).map_err(Error::BinrwError)
     }
 }
 
@@ -181,7 +200,15 @@ pub struct SpvOutline {
 }
 
 impl SpvOutline {
-    fn read_items<F, R>(self, archive: &mut SpvArchive<R>, warn: &mut F) -> Result<SpvFile, Error>
+    /// Filters the items in this outline using `criteria`.
+    pub fn filter(mut self, criteria: &Criteria) -> Self {
+        self.items = criteria.apply(take(&mut self.items));
+        self
+    }
+
+    /// Reads the full contents of the items in this outline from `archive`,
+    /// using `warn` to report any issues encountered along the way.
+    pub fn read<F, R>(self, archive: &mut SpvArchive<R>, warn: &mut F) -> Result<SpvFile, Error>
     where
         R: Read + Seek,
         F: FnMut(Warning),
@@ -191,13 +218,23 @@ impl SpvOutline {
             items: self
                 .items
                 .into_iter()
-                .map(|member| member.read_item(&mut archive.0, warn))
+                .map(|member| member.read_item(archive, warn))
                 .collect(),
         })
     }
+
+    /// Returns the contents of the `SpvOutline`.
+    pub fn into_contents(self) -> (Vec<OutlineItem>, Option<PageSetup>) {
+        (self.items, self.page_setup)
+    }
+
+    /// Returns just the [OutlineItem]s.
+    pub fn into_items(self) -> Vec<OutlineItem> {
+        self.items
+    }
 }
 
-/// A SPSS viewer (SPV) file read with [ReadOptions].
+/// A SPSS viewer (SPV) file.
 pub struct SpvFile {
     /// SPV file contents.
     pub items: Vec<Item>,
@@ -220,7 +257,7 @@ impl SpvFile {
 
 /// An error reading an SPV file.
 ///
-/// Returned by [ReadOptions::open_file] and [ReadOptions::open_reader].
+/// Returned by [SpvArchive::read].
 #[derive(Debug, Display, thiserror::Error)]
 pub enum Error {
     /// Not an SPV file.
@@ -255,8 +292,16 @@ pub enum Error {
     /// Legacy table missing `graph` element.
     LegacyMissingGraph,
 
-    /// Graphs not yet implemented.
-    GraphTodo,
+    /// Error parsing {member_name}: Unknown character encoding {encoding}.
+    UnknownEncoding {
+        /// The name of the file inside the ZIP file that caused the error.
+        member_name: String,
+        /// Unknown encoding.
+        encoding: String,
+    },
+
+    /// {0}
+    GraphError(#[from] graph::GraphError),
 
     /// Models not yet implemented.
     ModelTodo,
index abd7712d19ccc801f25168b621b5756511db0e30..628cb4ee33b84d4457ea30f92706eac78d1f0819 100644 (file)
@@ -176,7 +176,7 @@ impl FontStyle {
                     "font-weight" => self.bold = value == "bold",
                     "font-style" => self.italic = value == "italic",
                     "text-decoration" => self.underline = value == "underline",
-                    "font-family" => self.font = value.into(),
+                    "font-family" => self.font = Cow::from(value.clone().into_owned()),
                     "font-size" => {
                         if let Ok(size) = value.parse::<i32>() {
                             self.size = (size as i64 * 3 / 4) as i32;
diff --git a/rust/pspp/src/spv/read/graph.rs b/rust/pspp/src/spv/read/graph.rs
new file mode 100644 (file)
index 0000000..7efed85
--- /dev/null
@@ -0,0 +1,5764 @@
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program.  If not, see <http://www.gnu.org/licenses/>.
+
+//#![allow(dead_code, unused)]
+use std::{
+    borrow::{Borrow, Cow},
+    cell::RefCell,
+    collections::{BTreeMap, HashMap},
+    fmt::Debug,
+    io::{Cursor, ErrorKind},
+    iter::{repeat, zip},
+    mem::take,
+    num::NonZeroUsize,
+    ops::{Not, Range},
+    str::FromStr,
+};
+
+use cairo::{Context, RecordingSurface};
+use enum_iterator::all;
+use enum_map::{Enum, EnumMap, enum_map};
+use indexmap::{IndexMap, IndexSet};
+use itertools::Itertools;
+use num::{Integer, NumCast};
+use ordered_float::OrderedFloat;
+use pango::FontDescription;
+use quick_xml::{
+    DeError, Reader, Writer,
+    events::{BytesStart, Event},
+};
+use rs_stats::distributions::normal_distribution::normal_pdf;
+use serde::{Deserialize, Deserializer, de::Error as _};
+use smallvec::{SmallVec, smallvec};
+use svg::{
+    Document, Node,
+    node::element::{Circle, Definitions, Group, Line, Path, Polygon, Rectangle, path::Data},
+};
+
+use crate::{
+    data::Datum,
+    locale::{Item, Locale, data::EN},
+    output::{
+        SpvGraphMembers, SpvInfo, SpvMembers,
+        drivers::cairo::{pt_to_xr, xr_to_pt},
+        pivot::{
+            Axis2, Axis3, Length, ParseLengthError,
+            look::{Color, HorzAlign},
+        },
+    },
+    spv::{
+        legacy_bin::{DataSeries, DataSource, DataSources, LegacyBin, LegacyBinWarning},
+        read::graph::{
+            format::Formatter,
+            frames::{Component, FrameType, Frames, Position},
+            patterns::Patterns,
+            style::{Style, StyleCycle},
+            var_set::{Expression, Operator, VarSet, VarSetError},
+        },
+    },
+};
+
+mod format;
+mod frames;
+mod patterns;
+mod style;
+mod symbols;
+mod var_set;
+
+#[derive(Clone, Debug, Default)]
+struct Map(HashMap<Datum<String>, String>);
+
+impl Map {
+    fn apply(&self, data: &mut Vec<SeriesValue>) {
+        for value in data {
+            if let Some(to) = self.0.get(&value.value) {
+                value.label = Some(to.clone());
+            }
+        }
+    }
+}
+
+/// A warning decoding a graph member.
+#[derive(Clone, Debug, displaydoc::Display, thiserror::Error)]
+pub enum GraphWarning {
+    /// {0}
+    LegacyBinWarning(LegacyBinWarning),
+
+    /// Graph has delimitedSource but no CSV data was provided.
+    DelimitedSourceWithoutCsvData,
+
+    /// Graph has CSV data but no delimitedSource element.
+    CsvDataWithoutDelimitedSource,
+
+    /// Graph has variables with unresolved dependencies: {0:?}.
+    UnresolvedDependencies(Vec<String>),
+
+    /// Derived variable {variable:?} has unsupported value expression {value:?}.
+    UnsupportedValue {
+        /// Name of derived variable.
+        variable: String,
+        /// Value expression.
+        value: String,
+    },
+
+    /// Faceting expression has multiple top-level operands.
+    MultipleFacetOperands,
+}
+
+/// An error decoding a legacy graph.
+#[derive(Debug, thiserror::Error, displaydoc::Display)]
+pub enum GraphError {
+    /// Error parsing graph data: {0}
+    BinaryDataError(#[from] binrw::Error),
+
+    /// Error parsing XML data: {0}
+    XmlError(#[from] serde_path_to_error::Error<quick_xml::DeError>),
+
+    /// Graph references missing variable `{0}`.
+    MissingVariable(String),
+}
+
+#[derive(Debug)]
+pub struct Visualization {
+    /// Locale used for output, e.g. `en-US`.
+    lang: Option<String>,
+
+    /// The `id` for the base [Style] for the graph.
+    _style: Option<String>,
+
+    /// Title or caption.
+    _description: Option<String>,
+
+    /// VizML Schema version number.
+    _version: Option<String>,
+
+    id: Option<String>,
+
+    source_variables: Vec<SourceVariable>,
+    derived_variables: Vec<DerivedVariable>,
+    delimited_file_sources: Vec<DelimitedFileSource>,
+    embedded_sources: Vec<EmbeddedSource>,
+    categorical_domains: Vec<CategoricalDomain>,
+    interval_domains: Vec<IntervalDomain>,
+    children: Vec<AnyFrame>,
+    styles: Vec<Style>,
+    style_cycles: Vec<StyleCycle>,
+    locations: Vec<Location>,
+}
+
+impl<'de> Deserialize<'de> for Visualization {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        pub struct RawVisualization {
+            /// Locale used for output, e.g. `en-US`.
+            #[serde(rename = "@lang")]
+            lang: Option<String>,
+
+            /// The `id` for the base [Style] for the graph.
+            #[serde(rename = "@style")]
+            _style: Option<String>,
+
+            /// Title or caption.
+            #[serde(rename = "@description")]
+            _description: Option<String>,
+
+            /// VizML Schema version number.
+            #[serde(rename = "@version")]
+            _version: Option<String>,
+
+            #[serde(rename = "@id")]
+            id: Option<String>,
+
+            #[serde(rename = "$value")]
+            children: Vec<VisChild>,
+        }
+
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        enum VisChild {
+            SourceVariable(SourceVariable),
+            DerivedVariable(DerivedVariable),
+            /// This is a kind of stub for data that is supplied through another source.
+            /// In the corpus, these just have an `id` attribute, but there's no value
+            /// in parsing it.
+            UserSource,
+            DelimitedFileSource(DelimitedFileSource),
+            EmbeddedSource(EmbeddedSource),
+            CategoricalDomain(CategoricalDomain),
+            IntervalDomain(IntervalDomain),
+            Graph(Graph),
+            LabelFrame(LabelFrame),
+            Container(Container),
+            StyleCycle(StyleCycle),
+            Style(Style),
+            Location(Location),
+            #[serde(other)]
+            Other,
+        }
+
+        let RawVisualization {
+            lang,
+            _style,
+            _description,
+            _version,
+            id,
+            children,
+        } = RawVisualization::deserialize(deserializer)?;
+        let mut source_variables = Vec::new();
+        let mut derived_variables = Vec::new();
+        let mut delimited_file_sources = Vec::new();
+        let mut embedded_sources = Vec::new();
+        let mut categorical_domains = Vec::new();
+        let mut interval_domains = Vec::new();
+        let mut frames = Vec::new();
+        let mut styles = Vec::new();
+        let mut style_cycles = Vec::new();
+        let mut locations = Vec::new();
+
+        for child in children {
+            match child {
+                VisChild::SourceVariable(source_variable) => source_variables.push(source_variable),
+                VisChild::DerivedVariable(derived_variable) => {
+                    derived_variables.push(derived_variable)
+                }
+                VisChild::UserSource => (),
+                VisChild::DelimitedFileSource(delimited_file_source) => {
+                    delimited_file_sources.push(delimited_file_source)
+                }
+                VisChild::EmbeddedSource(embedded_source) => embedded_sources.push(embedded_source),
+                VisChild::CategoricalDomain(categorical_domain) => {
+                    categorical_domains.push(categorical_domain)
+                }
+                VisChild::IntervalDomain(interval_domain) => interval_domains.push(interval_domain),
+                VisChild::Graph(graph) => frames.push(AnyFrame::Graph(graph)),
+                VisChild::LabelFrame(label_frame) => frames.push(AnyFrame::LabelFrame(label_frame)),
+                VisChild::Container(container) => frames.push(AnyFrame::Container(container)),
+                VisChild::StyleCycle(style_cycle) => style_cycles.push(style_cycle),
+                VisChild::Style(style) => styles.push(style),
+                VisChild::Location(location) => locations.push(location),
+                VisChild::Other => (),
+            }
+        }
+        Ok(Self {
+            lang,
+            _style,
+            _description,
+            _version,
+            id,
+            source_variables,
+            derived_variables,
+            delimited_file_sources,
+            embedded_sources,
+            categorical_domains,
+            interval_domains,
+            children: frames,
+            styles,
+            style_cycles,
+            locations,
+        })
+    }
+}
+
+impl Visualization {
+    fn all_variables(&self) -> impl Iterator<Item = &dyn Variable> {
+        let source_variables = self
+            .source_variables
+            .iter()
+            .map(|variable| variable as &dyn Variable);
+        let derived_variables = self
+            .derived_variables
+            .iter()
+            .map(|variable| variable as &dyn Variable);
+        source_variables.chain(derived_variables)
+    }
+
+    fn decode_series(
+        &self,
+        locale: &'static Locale<'static>,
+        data: DataSources,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> BTreeMap<&str, Series> {
+        let mut variables = self.all_variables().collect_vec();
+        let mut series = BTreeMap::<&str, Series>::new();
+        while !variables.is_empty() {
+            let n = variables.len();
+            variables.retain(|variable| !variable.decode(locale, &data, &mut series, warn));
+            if n == variables.len() {
+                warn(GraphWarning::UnresolvedDependencies(
+                    variables
+                        .iter()
+                        .map(|variable| variable.name().into())
+                        .collect(),
+                ));
+                break;
+            }
+        }
+        series
+    }
+
+    fn all_domains(&self) -> impl Iterator<Item = &dyn RawDomain> {
+        let categorical_domains = self
+            .categorical_domains
+            .iter()
+            .map(|domain| domain as &dyn RawDomain);
+        let interval_domains = self
+            .interval_domains
+            .iter()
+            .map(|domain| domain as &dyn RawDomain);
+        categorical_domains.chain(interval_domains)
+    }
+
+    fn decode_domains(
+        &self,
+        series: &BTreeMap<&str, Series>,
+        _warn: &mut dyn FnMut(GraphWarning),
+    ) -> BTreeMap<&str, Domain> {
+        let mut inputs = self.all_domains().collect_vec();
+        let mut domains = BTreeMap::<&str, Domain>::new();
+        while !inputs.is_empty() {
+            let n = inputs.len();
+            inputs.retain(|input| !input.decode(series, &mut domains));
+            if n == inputs.len() {
+                todo!()
+            }
+        }
+        domains
+    }
+
+    fn decode_styles(&self) -> HashMap<String, Style> {
+        fn inner(mut style: Style, styles: &mut HashMap<String, Style>) {
+            for child in take(&mut style.children) {
+                inner(style.clone().overridden_with(&child), styles);
+            }
+            if let Some(id) = &style.id {
+                styles.insert(id.clone(), style);
+            }
+        }
+
+        let mut styles = HashMap::new();
+        for node in &self.styles {
+            inner(node.clone(), &mut styles);
+        }
+        styles
+    }
+
+    fn decode_style_cycles(&self) -> HashMap<&str, &StyleCycle> {
+        self.style_cycles
+            .iter()
+            .filter_map(|style_cycle| style_cycle.id.as_deref().map(|s| (s, style_cycle)))
+            .collect()
+    }
+
+    fn pass<'a>(
+        &'a self,
+        graph: &'a Graph,
+        locale: &'static Locale<'static>,
+        domains: &BTreeMap<&str, Domain>,
+        series: &BTreeMap<&str, Series>,
+        bin_widths: &mut BTreeMap<String, f64>,
+        axis_domains: &mut EnumMap<Axis3, AxisDomain<'a>>,
+        styles: &'a HashMap<String, Style>,
+    ) -> Result<Vec<Element<'a>>, GraphError> {
+        let elements = graph
+            .elements
+            .iter()
+            .map(|raw| Element::new(raw, locale, series, bin_widths, axis_domains, styles))
+            .collect::<Result<Vec<_>, _>>()?;
+        let dimensions = &graph.coordinates.dimensions;
+        for axis in all() {
+            let mut domain = if let Some(dimension) = &dimensions[axis]
+                && let Some(domain_name) = &dimension._domain
+                && let Some(domain) = domains.get(domain_name.as_str())
+            {
+                Some(domain.clone())
+            } else {
+                None
+            };
+            let mut formatter = None;
+            for element in &elements {
+                let Some(variable) = &element.variables[axis] else {
+                    continue;
+                };
+                if formatter.is_none() {
+                    formatter = Some(variable.formatter.clone());
+                }
+                if !element.ignore_minmax[axis] {
+                    let domain = domain.get_or_insert_with(|| {
+                        if variable.categorical {
+                            Domain::new_categorical()
+                        } else {
+                            Domain::new_interval()
+                        }
+                    });
+                    match domain {
+                        Domain::Interval { min, max, exact } => {
+                            if !*exact || min.is_none() || max.is_none() {
+                                for value in &variable.values {
+                                    if let Some(Some(number)) = value.value.as_number() {
+                                        if min.is_none_or(|min| number < min) {
+                                            *min = Some(number);
+                                        }
+                                        if max.is_none_or(|max| number > max) {
+                                            *max = Some(number);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        Domain::Categorical { values } => {
+                            for value in &variable.values {
+                                values
+                                    .entry(value.value.clone())
+                                    .or_insert_with(|| value.label.clone());
+                            }
+                        }
+                    }
+                }
+            }
+            let mut domain = domain.unwrap_or_default();
+            if let Domain::Interval { min, max, exact } = &mut domain
+                && (!*exact || min.is_none() || max.is_none())
+            {
+                match min {
+                    Some(number) => {
+                        if *number > 0.0 {
+                            *min = Some(0.0)
+                        }
+                    }
+                    None => {
+                        *min = Some(0.0);
+                        *max = Some(0.0);
+                    }
+                }
+                if let Some(number) = max
+                    && *number < 0.0
+                {
+                    *max = Some(0.0);
+                }
+                if let Some(dimension) = &dimensions[axis] {
+                    let mmin = min.unwrap();
+                    let mmax = max.unwrap();
+                    let range = mmax - mmin;
+                    if let Some(LocationLength::Percent(percent)) = dimension.lower_margin {
+                        *min = Some(mmin - range * (percent / 100.0));
+                    }
+                    if let Some(LocationLength::Percent(percent)) = dimension.upper_margin {
+                        *max = Some(mmax + range * (percent / 100.0));
+                    }
+                }
+            }
+
+            let axis2 = dimensions[axis]
+                .as_ref()
+                .and_then(|dimension| dimension.axis.first());
+            axis_domains[axis] = AxisDomain {
+                domain,
+                formatter: formatter.unwrap_or_else(|| Formatter::default_for_locale(locale)),
+                axis: axis2,
+                _label: axis2.and_then(|axis| axis.label.as_ref()),
+                major_ticks: axis2.and_then(|axis| axis.major_ticks.as_ref()),
+            };
+        }
+        Ok(elements)
+    }
+
+    pub fn decode(
+        &self,
+        mut data: DataSources,
+        mut csv_data: Option<DataSource>,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> Result<svg::Document, GraphError> {
+        let style_cycles = self.decode_style_cycles();
+        for delimited_source in &self.delimited_file_sources {
+            if let Some(csv_data) = csv_data.take() {
+                data.0.insert(delimited_source.id.clone(), csv_data);
+            } else {
+                warn(GraphWarning::DelimitedSourceWithoutCsvData);
+            }
+        }
+        if csv_data.is_some() {
+            warn(GraphWarning::CsvDataWithoutDelimitedSource);
+        }
+        for embedded_source in &self.embedded_sources {
+            data.0
+                .insert(embedded_source.id.clone(), embedded_source.decode());
+        }
+        let locale = self
+            .lang
+            .as_ref()
+            .map_or(&EN, |lang| Locale::for_lang(lang));
+        let series = self.decode_series(locale, data, warn);
+        let domains = self.decode_domains(&series, warn);
+        let styles = self.decode_styles();
+        let frames = self.decode_frames();
+
+        let mut document = svg::Document::new()
+            .set("xmlns:c", "http://www.example.org/")
+            .set("style", "font-family: \"Sans Serif\"");
+        if let Some(left) = frames.frames[0].position[Axis2::X].start()
+            && let Some(width) = frames.frames[0].position[Axis2::X].length()
+            && let Some(top) = frames.frames[0].position[Axis2::Y].start()
+            && let Some(height) = frames.frames[0].position[Axis2::Y].length()
+        {
+            document.assign("viewBox", (left, top, left + width, top + height));
+            document.assign("width", width);
+            document.assign("height", height);
+        }
+
+        let outline_frames = false;
+        if outline_frames {
+            document.append(frames.to_svg());
+        }
+
+        let font_metrics = FontMetrics::new();
+        for frame in &frames.frames {
+            if let Some(label_frame) = frame.frame_type.as_label_frame()
+                && let Some(left) = frame.position[Axis2::X].start()
+                && let Some(right) = frame.position[Axis2::X].end()
+                && let Some(top) = frame.position[Axis2::Y].start()
+                && let Some(_height) = frame.position[Axis2::Y].length()
+                && let Some(label) = &label_frame.label
+            {
+                let text = label.text().unwrap_or_default();
+                let _metrics = font_metrics.measure(&text);
+                let text_alignment = label
+                    .style
+                    .as_deref()
+                    .and_then(|style| styles.get(style))
+                    .and_then(|style| style.label_horz_align())
+                    .unwrap_or(HorzAlign::Center);
+                let (x, text_anchor) = match text_alignment {
+                    HorzAlign::Left | HorzAlign::Decimal { .. } => (left, "start"),
+                    HorzAlign::Right => (right, "end"),
+                    HorzAlign::Center => (left.midpoint(right), "middle"),
+                };
+                document.append(
+                    svg::node::element::Text::new(text)
+                        .with_id(&label_frame.id)
+                        .set("x", x)
+                        .set("y", top)
+                        .set("dominant-baseline", "hanging")
+                        .set("text-anchor", text_anchor)
+                        .set("font-family", "sans-serif"),
+                ); /*
+                document.append(
+                Rectangle::new()
+                .set("x", left)
+                .set("width", metrics[Axis2::X])
+                .set("y", top)
+                .set("height", metrics[Axis2::Y])
+                .set("fill", "none")
+                .set("stroke", "black"),
+                );*/
+            }
+        }
+
+        for (graph_frame, graph) in frames.graphs() {
+            document.append(self.decode_graph(
+                graph_frame,
+                graph,
+                locale,
+                &domains,
+                &series,
+                &style_cycles,
+                &styles,
+                warn,
+            )?);
+        }
+
+        Ok(document)
+    }
+
+    fn decode_graph(
+        &self,
+        graph_frame: &Frame,
+        graph: &Graph,
+        locale: &'static Locale<'static>,
+        domains: &BTreeMap<&str, Domain>,
+        series: &BTreeMap<&str, Series>,
+        style_cycles: &HashMap<&str, &StyleCycle>,
+        styles: &HashMap<String, Style>,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> Result<svg::node::element::Group, GraphError> {
+        for element in &graph.elements {
+            dbg!(element.var_set(graph.faceting.as_ref(), series, warn));
+        }
+        let mut graph_svg = Group::new().with_id(&graph.id);
+        let mut bin_widths = BTreeMap::new();
+        let mut axis_domains =
+            EnumMap::<Axis3, AxisDomain>::from_fn(|_| AxisDomain::default_for_locale(locale));
+        let mut patterns = Patterns::new();
+        self.pass(
+            &graph,
+            locale,
+            &domains,
+            &series,
+            &mut bin_widths,
+            &mut axis_domains,
+            styles,
+        )?;
+        //dbg!(&axis_domains);
+        let elements = self.pass(
+            &graph,
+            locale,
+            &domains,
+            &series,
+            &mut bin_widths,
+            &mut axis_domains,
+            styles,
+        )?;
+        //dbg!(&axis_domains);
+
+        let font_metrics = FontMetrics::new();
+        let rect = graph_frame.rect().unwrap();
+
+        let cell_style = graph
+            .cell_style
+            .as_deref()
+            .and_then(|cell_style| styles.get(cell_style))
+            .cloned()
+            .unwrap_or_default();
+
+        let facet_levels = graph.facet_levels();
+        let faceting = graph
+            .faceting
+            .as_ref()
+            .map(|f| f.decode(&series, &facet_levels))
+            .unwrap_or_default();
+
+        let x_facets = FacetIterator::new(&faceting[Axis2::X]).into_values();
+        let nx_facets = x_facets.len();
+        let y_facets = FacetIterator::new(&faceting[Axis2::Y]).into_values();
+        let ny_facets = y_facets.len();
+
+        let mut heading_heights = EnumMap::from_fn(|_| 0.0);
+        let mut iteration = 0..5;
+        let headings = loop {
+            let headings = EnumMap::from_fn(|axis| {
+                let heading = FacetHeadings::new(
+                    Side::default_for_axis(axis),
+                    &faceting[axis],
+                    &axis_domains[axis.into()],
+                    rect.0[axis].length() - heading_heights[!axis],
+                    graph.axis_dimension(axis.into()),
+                    &font_metrics,
+                    axis == Axis2::Y,
+                );
+                heading_heights[axis] = heading.total_height();
+                heading
+            });
+            if iteration.next().is_none() {
+                break headings;
+            }
+        };
+
+        let inner = rect.inset(
+            [
+                headings[Axis2::Y].height(Side::Top),
+                headings[Axis2::Y].height(Side::Bottom),
+            ],
+            [
+                headings[Axis2::X].height(Side::Top),
+                headings[Axis2::X].height(Side::Bottom),
+            ],
+        );
+
+        let top = headings[Axis2::X]
+            .render(Side::Top, 0.0)
+            .set("c:description", "X axis facets above graph")
+            .set(
+                "transform",
+                format!("translate({} {})", inner.0[Axis2::X].0, rect.0[Axis2::Y].0),
+            );
+        let bottom = headings[Axis2::X]
+            .render(Side::Bottom, 0.0)
+            .set("c:description", "X axis facets below graph")
+            .set(
+                "transform",
+                format!("translate({} {})", inner.0[Axis2::X].0, inner.0[Axis2::Y].1,),
+            );
+
+        let left = headings[Axis2::Y]
+            .render(Side::Top, -90.0)
+            .set("c:description", "Y axis facets on left side of graph")
+            .set("transform", {
+                let x = rect.0[Axis2::X].0;
+                let y = inner.0[Axis2::Y].1;
+                format!("rotate(-90 {x} {y}) translate({x} {y})")
+            });
+        let right = headings[Axis2::Y]
+            .render(Side::Bottom, -90.0)
+            .set("c:description", "Y axis facets on right side of graph")
+            .set("transform", {
+                let x = rect.0[Axis2::X].1;
+                let y = inner.0[Axis2::Y].0;
+                format!("rotate(90 {x} {y}) translate({x} {y})")
+            });
+
+        for (x_index, x_facet) in x_facets.enumerate() {
+            for (y_index, y_facet) in y_facets.clone().enumerate() {
+                let rect = inner.subrect(x_index, nx_facets, y_index, ny_facets);
+
+                // If the graph has a colored cell style, outline the whole
+                // graph in a rectangle with that style.  Otherwise, draw X and
+                // Y axes.
+                if cell_style.color.is_some() {
+                    graph_svg.append(
+                        Rectangle::new()
+                            .set("x", rect.0[Axis2::X].0)
+                            .set("y", rect.0[Axis2::Y].0)
+                            .set("width", rect.0[Axis2::X].length())
+                            .set("height", rect.0[Axis2::Y].length())
+                            .set("fill", cell_style.fill(&mut patterns))
+                            .set(
+                                "stroke",
+                                cell_style.color2(Color::BLACK).display_css().to_string(),
+                            ),
+                    );
+                } else {
+                    graph_svg.append(
+                        Line::new()
+                            .set("x1", rect.0[Axis2::X].0)
+                            .set("y1", rect.0[Axis2::Y].0)
+                            .set("x2", rect.0[Axis2::X].0)
+                            .set("y2", rect.0[Axis2::Y].1)
+                            .set("fill", cell_style.fill(&mut patterns))
+                            .set(
+                                "stroke",
+                                cell_style.color2(Color::BLACK).display_css().to_string(),
+                            ),
+                    );
+                    graph_svg.append(
+                        Line::new()
+                            .set("x1", rect.0[Axis2::X].0)
+                            .set("y1", rect.0[Axis2::Y].1)
+                            .set("x2", rect.0[Axis2::X].1)
+                            .set("y2", rect.0[Axis2::Y].1)
+                            .set("fill", cell_style.fill(&mut patterns))
+                            .set(
+                                "stroke",
+                                cell_style.color2(Color::BLACK).display_css().to_string(),
+                            ),
+                    );
+                }
+
+                for element in &elements {
+                    let mut categoricals = IndexMap::new();
+                    let mut color_index = None;
+                    if let Some(color) = element
+                        .raw
+                        .color
+                        .iter()
+                        .find(|color| color.affect.unwrap_or_default().includes_main())
+                        && let Some(variable) = &color.variable
+                    {
+                        let series = series
+                            .get(variable.as_str())
+                            .ok_or_else(|| GraphError::MissingVariable(variable.clone()))?;
+                        if series.categorical {
+                            color_index =
+                                Some(categoricals.insert_full(series.name.as_str(), series).0);
+                        }
+                    }
+                    let mut color2_index = None;
+                    if let Some(color2) = element
+                        .raw
+                        .color
+                        .iter()
+                        .find(|color| color.affect.unwrap_or_default().includes_secondary())
+                        && let Some(variable) = &color2.variable
+                    {
+                        let series = series
+                            .get(variable.as_str())
+                            .ok_or_else(|| GraphError::MissingVariable(variable.clone()))?;
+                        if series.categorical {
+                            color2_index =
+                                Some(categoricals.insert_full(series.name.as_str(), series).0);
+                        }
+                    }
+                    let mut styling = None;
+                    if let Some(style_by) = element.raw.style_by.first()
+                        && let Some(variable) = &style_by.variable
+                    {
+                        let series = series
+                            .get(variable.as_str())
+                            .ok_or_else(|| GraphError::MissingVariable(variable.clone()))?;
+                        if series.categorical {
+                            let style_by_var_index =
+                                categoricals.insert_full(series.name.as_str(), series).0;
+                            if let Some(style_cycle_id) = style_by.style_cycle.as_deref()
+                                && let Some(style_cycle) = style_cycles.get(style_cycle_id).copied()
+                            {
+                                styling = Some((style_by_var_index, style_cycle));
+                            }
+                        }
+                    }
+                    let categoricals = categoricals.into_values().collect_vec();
+
+                    let mut grouped_data = BTreeMap::<_, Vec<_>>::new();
+                    'values: for i in 0.. {
+                        for (index, facet) in faceting[Axis2::X].iter().enumerate() {
+                            let Some(value) = facet.series.values.get(i) else {
+                                break 'values;
+                            };
+                            let expected = x_facet[index];
+                            if expected.value != value.value {
+                                continue 'values;
+                            }
+                        }
+                        for (index, facet) in faceting[Axis2::Y].iter().enumerate() {
+                            let Some(value) = facet.series.values.get(i) else {
+                                break 'values;
+                            };
+                            let expected = y_facet[index];
+                            if expected.value != value.value {
+                                continue 'values;
+                            }
+                        }
+                        let categorical_values = categoricals
+                            .iter()
+                            .flat_map(|series| series.values.get(i))
+                            .map(|sv| sv.value.clone())
+                            .collect_vec();
+                        if categorical_values.len() != categoricals.len() {
+                            break;
+                        }
+                        let values = element
+                            .variables
+                            .values()
+                            .flatten()
+                            .flat_map(|series| series.values.get(i))
+                            .collect_vec();
+                        if values.len() != element.variables.values().flatten().count() {
+                            break;
+                        }
+
+                        grouped_data
+                            .entry(categorical_values)
+                            .or_default()
+                            .push(values);
+                    }
+                    for (categoricals, group) in &grouped_data {
+                        let mut style = element.style.clone().into_owned();
+                        if let Some((index, style_cycle)) = styling {
+                            let index = categoricals[index]
+                                .as_number()
+                                .unwrap_or_default()
+                                .unwrap_or_default();
+                            let index = <usize as NumCast>::from(index).unwrap_or_default();
+                            for s in style_cycle.get_style_components(index) {
+                                style = style.overridden_with(s);
+                            }
+                        }
+                        if let Some(color_index) = color_index {
+                            let index = categoricals[color_index]
+                                .as_number()
+                                .unwrap_or_default()
+                                .unwrap_or_default();
+                            let index = <usize as NumCast>::from(index).unwrap_or_default();
+                            style.color = Some(PALETTE[index % PALETTE.len()]);
+                        }
+                        if let Some(color2_index) = color2_index {
+                            let index = categoricals[color2_index]
+                                .as_number()
+                                .unwrap_or_default()
+                                .unwrap_or_default();
+                            let index = <usize as NumCast>::from(index).unwrap_or_default();
+                            style.color2 = Some(PALETTE[index % PALETTE.len()]);
+                        }
+
+                        let color = style.color(Color::BLACK);
+                        let color2 = style.color2(Color::BLACK);
+                        let pattern = style.fill(&mut patterns);
+
+                        fn points(
+                            rect: &Rect,
+                            group: &[Vec<&SeriesValue>],
+                            axis_domains: &EnumMap<Axis3, AxisDomain>,
+                        ) -> impl Iterator<Item = (f64, f64)> {
+                            group.iter().filter_map(|values| {
+                                let x = axis_domains[Axis3::X]
+                                    .domain
+                                    .lookup(&values.get(0)?.value)?;
+                                let x = rect.0[Axis2::X].frac(x);
+
+                                let y = match values.get(1) {
+                                    Some(value) => {
+                                        axis_domains[Axis3::Y].domain.lookup(&value.value)?
+                                    }
+                                    None => 0.5,
+                                };
+                                let y = rect.0[Axis2::Y].frac(1.0 - y);
+                                Some((x, y))
+                            })
+                        }
+
+                        fn ranges(
+                            rect: &Rect,
+                            group: &[Vec<&SeriesValue>],
+                            axis_domains: &EnumMap<Axis3, AxisDomain>,
+                        ) -> impl Iterator<Item = (Range<f64>, f64)> {
+                            group.iter().filter_map(|values| {
+                                let x = axis_domains[Axis3::X]
+                                    .domain
+                                    .lookup_range(&values.get(0)?.value)?;
+                                let x =
+                                    rect.0[Axis2::X].frac(x.start)..rect.0[Axis2::X].frac(x.end);
+
+                                let y = match values.get(1) {
+                                    Some(value) => {
+                                        axis_domains[Axis3::Y].domain.lookup(&value.value)?
+                                    }
+                                    None => 0.5,
+                                };
+                                let y = rect.0[Axis2::Y].frac(1.0 - y);
+                                Some((x, y))
+                            })
+                        }
+
+                        let mut element_svg = Group::new().with_id(&element.raw.id);
+                        match element.raw.graphic {
+                            Graphic::Interval => {
+                                // Size defaults to 33%.
+                                let fraction = match style.symbol_size {
+                                    Some(LocationLength::Percent(percent)) => percent,
+                                    _ => 33.0,
+                                } / 100.0
+                                    / 2.0;
+                                for (x, y0) in ranges(&rect, group, &axis_domains) {
+                                    let width = x.end - x.start;
+                                    let midpoint = x.start.midpoint(x.end);
+                                    let x =
+                                        midpoint - width * fraction..midpoint + width * fraction;
+                                    let y1 = rect.0[Axis2::Y].1;
+                                    element_svg.append(
+                                        Rectangle::new()
+                                            .set("fill", pattern.clone())
+                                            .set("stroke", color2.display_css().to_string())
+                                            .set("x", x.start)
+                                            .set("y", y0)
+                                            .set("width", (x.end - x.start).max(1.0))
+                                            .set("height", y1 - y0),
+                                    );
+                                }
+                            }
+                            Graphic::Point => {
+                                for (x, y) in points(&rect, group, &axis_domains) {
+                                    element_svg.append(
+                                        Circle::new()
+                                            .set("cx", x)
+                                            .set("cy", y)
+                                            .set("r", "5px")
+                                            .set("fill", "none")
+                                            .set("stroke", "black"),
+                                    );
+                                }
+                            }
+                            Graphic::Schema => {
+                                let points = points(&rect, group, &axis_domains);
+                                let boxes = points.chunk_by(|(x, _y)| *x);
+                                for (x, mut points) in &boxes {
+                                    if let Some((_, median)) = points.next()
+                                        && let Some((_, lower_hinge)) = points.next()
+                                        && let Some((_, upper_hinge)) = points.next()
+                                        && let Some((_, lower_fence)) = points.next()
+                                        && let Some((_, upper_fence)) = points.next()
+                                    {
+                                        element_svg.append(
+                                            Line::new()
+                                                .set("x1", x - 5.0)
+                                                .set("x2", x + 5.0)
+                                                .set("y1", median)
+                                                .set("y2", median)
+                                                .set("fill", "none")
+                                                .set("stroke", "black")
+                                                .set("stroke-width", 2),
+                                        );
+                                        element_svg.append(
+                                            Rectangle::new()
+                                                .set("x", x - 5.0)
+                                                .set("width", 10.0)
+                                                .set("y", upper_hinge)
+                                                .set("height", lower_hinge - upper_hinge)
+                                                .set("fill", "none")
+                                                .set("stroke", "black"),
+                                        );
+                                        element_svg.append(
+                                            Line::new()
+                                                .set("x1", x)
+                                                .set("x2", x)
+                                                .set("y1", upper_fence)
+                                                .set("y2", upper_hinge)
+                                                .set("fill", "none")
+                                                .set("stroke", "black"),
+                                        );
+                                        element_svg.append(
+                                            Line::new()
+                                                .set("x1", x)
+                                                .set("x2", x)
+                                                .set("y1", lower_fence)
+                                                .set("y2", lower_hinge)
+                                                .set("fill", "none")
+                                                .set("stroke", "black"),
+                                        );
+                                        for y in [lower_fence, upper_fence] {
+                                            element_svg.append(
+                                                Line::new()
+                                                    .set("x1", x - 5.0)
+                                                    .set("x2", x + 5.0)
+                                                    .set("y1", y)
+                                                    .set("y2", y)
+                                                    .set("fill", "none")
+                                                    .set("stroke", "black"),
+                                            );
+                                        }
+                                        for (x, y) in points {
+                                            element_svg.append(
+                                                Circle::new()
+                                                    .set("cx", x)
+                                                    .set("cy", y)
+                                                    .set("r", "5px")
+                                                    .set("fill", "none")
+                                                    .set("stroke", "black"),
+                                            );
+                                        }
+                                    }
+                                }
+                            }
+                            Graphic::Line | Graphic::Path => {
+                                let mut data = Data::new();
+                                for (i, (x, y)) in points(&rect, group, &axis_domains).enumerate() {
+                                    if i == 0 {
+                                        data = data.move_to((x, y));
+                                    } else {
+                                        data = data.line_to((x, y));
+                                    }
+                                }
+                                element_svg.append(
+                                    Path::new()
+                                        .set("fill", "none")
+                                        .set("stroke", color.display_css().to_string())
+                                        .set("d", data),
+                                );
+                            }
+                            Graphic::Polygon => todo!(),
+                            Graphic::Area => {
+                                let zero = axis_domains[Axis3::Y]
+                                    .domain
+                                    .lookup(&Datum::Number(Some(0.0)))
+                                    .unwrap_or(0.5);
+                                let zero = rect.0[Axis2::Y].frac(1.0 - zero);
+                                let mut data = Vec::new();
+                                for (i, (x, y)) in points(&rect, group, &axis_domains).enumerate() {
+                                    if i == 0 {
+                                        data.extend([x, zero]);
+                                    }
+                                    data.extend([x, y]);
+                                }
+                                if !data.is_empty() {
+                                    let last_x = data[data.len() - 2];
+                                    data.extend([last_x, zero]);
+                                }
+                                element_svg.append(
+                                    Polygon::new()
+                                        .set("fill", pattern)
+                                        .set("stroke", color2.display_css().to_string())
+                                        .set("points", data),
+                                );
+                            }
+                            Graphic::Edge => todo!(),
+                        }
+                        graph_svg.append(element_svg);
+                    }
+                }
+            }
+        }
+        let mut defs = Definitions::new();
+        for node in patterns.to_svg() {
+            defs.append(node);
+        }
+        if let Some(children) = defs.get_children()
+            && !children.is_empty()
+        {
+            graph_svg.append(defs);
+        }
+        graph_svg.append(top);
+        graph_svg.append(bottom);
+        graph_svg.append(left);
+        graph_svg.append(right);
+        //graph_svg.append(rect.as_svg().set("fill", "none").set("stroke", "red"));
+        //graph_svg.append(inner.as_svg().set("fill", "none").set("stroke", "green"));
+        Ok(graph_svg)
+    }
+}
+
+#[derive(Debug)]
+enum AnyFrame {
+    Container(Container),
+    Graph(Graph),
+    Legend(Legend),
+    LabelFrame(LabelFrame),
+}
+
+struct FontMetrics {
+    layout: RefCell<pango::Layout>,
+}
+
+impl FontMetrics {
+    fn new() -> Self {
+        let surface = RecordingSurface::create(cairo::Content::ColorAlpha, None).unwrap();
+        let context = Context::new(&surface).unwrap();
+        let layout = pangocairo::functions::create_layout(&context);
+        layout.set_font_description(Some(&FontDescription::from_string("sans-serif")));
+        Self {
+            layout: RefCell::new(layout),
+        }
+    }
+    fn measure(&self, s: &str) -> EnumMap<Axis2, f64> {
+        let layout = self.layout.borrow_mut();
+        layout.set_text(s);
+        let (w, h) = layout.size();
+        enum_map! { Axis2::X => xr_to_pt(w as isize), Axis2::Y => xr_to_pt(h as isize) }
+    }
+    fn measure_height_for_width(&self, s: &str, width: f64) -> f64 {
+        let layout = self.layout.borrow_mut();
+        layout.set_width(pt_to_xr(width) as i32);
+        layout.set_text(s);
+        let (_w, h) = layout.size();
+        xr_to_pt(h as isize)
+    }
+}
+
+#[derive(Clone, Debug)]
+struct SeriesValue {
+    value: Datum<String>,
+    label: Option<String>,
+}
+
+impl SeriesValue {
+    fn label(&self, formatter: &Formatter) -> String {
+        if let Some(label) = &self.label {
+            label.clone()
+        } else {
+            formatter.display(&self.value).to_string()
+        }
+    }
+}
+
+impl<T> From<T> for SeriesValue
+where
+    Datum<String>: From<T>,
+{
+    fn from(value: T) -> Self {
+        Self {
+            value: Datum::from(value),
+            label: None,
+        }
+    }
+}
+
+#[derive(Clone)]
+struct Series {
+    pub name: String,
+    _label: Option<String>,
+    _short_label: Option<String>,
+    _weight: bool,
+    _key: bool,
+    categorical: bool,
+    values: Vec<SeriesValue>,
+    unique_values: Vec<SeriesValue>,
+    formatter: Formatter,
+}
+
+impl Debug for Series {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Series")
+            .field("name", &self.name)
+            .field("categorical", &self.categorical)
+            .finish_non_exhaustive()
+    }
+}
+
+impl Series {
+    fn new(
+        name: String,
+        values: Vec<SeriesValue>,
+        categorical: bool,
+        formatter: Formatter,
+    ) -> Self {
+        let unique_values = if categorical {
+            values
+                .iter()
+                .unique_by(|sv| sv.value.clone())
+                .sorted_by(|a, b| a.value.cmp(&b.value))
+                .cloned()
+                .collect()
+        } else {
+            Vec::new()
+        };
+        Self {
+            name,
+            _label: None,
+            _short_label: None,
+            _weight: false,
+            _key: false,
+            categorical,
+            values,
+            unique_values,
+            formatter,
+        }
+    }
+
+    fn new_empty() -> Self {
+        Self::new(
+            String::from("empty"),
+            vec![],
+            false,
+            Formatter::default_for_locale(Locale::for_lang("en_US")),
+        )
+    }
+
+    fn with_label(self, label: Option<String>) -> Self {
+        Self {
+            _label: label,
+            ..self
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DelimitedFileSource {
+    /// Source name.
+    #[serde(rename = "@id")]
+    pub id: String,
+
+    /// Always "UTF-8" in the corpus.
+    #[serde(rename = "@encoding")]
+    pub _encoding: String,
+
+    /// A filename or URI-like string that probably names on a file on the host
+    /// of the person who created the graph.
+    #[serde(rename = "@fileName")]
+    pub _file_name: Option<String>,
+
+    #[serde(rename = "@missing")]
+    pub _missing: DelimitedFileSourceMissing,
+}
+
+impl Default for DelimitedFileSource {
+    fn default() -> Self {
+        Self {
+            id: String::new(),
+            _encoding: String::from("UTF-8"),
+            _file_name: None,
+            _missing: DelimitedFileSourceMissing::default(),
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct EmbeddedSource {
+    /// Source name.
+    #[serde(rename = "@id")]
+    pub id: String,
+
+    /// Variable names, semicolon separated.
+    #[serde(deserialize_with = "deserialize_semicolon_separated")]
+    pub names: Vec<String>,
+
+    /// Variable types, semicolon separated.
+    ///
+    /// Valid types are `int`, `double`, and `string`.
+    ///
+    /// PSPP uses types to avoid parsing `string`s as numbers.
+    #[serde(deserialize_with = "deserialize_semicolon_separated")]
+    #[serde(default)]
+    pub types: Vec<String>,
+
+    /// Variable labels, semicolon separated.
+    #[serde(deserialize_with = "deserialize_semicolon_separated")]
+    #[serde(default)]
+    pub labels: Vec<String>,
+
+    /// Data rows, semicolon separated.
+    #[serde(rename = "row")]
+    #[serde(default)]
+    pub rows: Vec<String>,
+}
+
+fn deserialize_semicolon_separated<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    Ok(SemicolonSplit::new(&String::deserialize(deserializer)?)
+        .map(|s| s.into_owned())
+        .collect())
+}
+
+impl EmbeddedSource {
+    fn from_xml(xml: &[u8]) -> Result<Self, DeError> {
+        quick_xml::de::from_reader(Cursor::new(xml))
+    }
+
+    fn decode(&self) -> DataSource {
+        let mut series = (0..self.names.len())
+            .map(|i| DataSeries {
+                label: self.labels.get(i).cloned(),
+                data: Vec::new(),
+            })
+            .collect_vec();
+        let is_string = self.types.iter().map(|s| s == "string").collect_vec();
+        for row in &self.rows {
+            for ((series, value), is_string) in zip(
+                &mut series,
+                SemicolonSplit::new(&row).chain(repeat(Cow::from(""))),
+            )
+            .zip(is_string.iter().copied().chain(repeat(false)))
+            {
+                let datum = if !is_string && let Ok(number) = value.parse() {
+                    Datum::Number(Some(number))
+                } else {
+                    Datum::String(value.into())
+                };
+                series.data.push(datum);
+            }
+        }
+        DataSource(zip(self.names.iter().cloned(), series).collect())
+    }
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum DelimitedFileSourceMissing {
+    #[default]
+    Pairwise,
+}
+
+trait RawDomain: Debug {
+    fn decode<'a>(
+        &'a self,
+        series: &BTreeMap<&str, Series>,
+        domains: &mut BTreeMap<&'a str, Domain>,
+    ) -> bool;
+}
+
+trait Variable {
+    fn decode<'a>(
+        &'a self,
+        locale: &'static Locale<'static>,
+        data: &DataSources,
+        series: &mut BTreeMap<&'a str, Series>,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> bool;
+    fn name(&self) -> &str;
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SourceVariable {
+    #[serde(rename = "@id")]
+    id: String,
+
+    /// The `source-name` in the `tableData.bin` member.
+    #[serde(rename = "@source")]
+    source: String,
+
+    /// The name of a variable within the source, corresponding to the
+    /// `variable-name` in the `tableData.bin` member.
+    #[serde(rename = "@sourceName")]
+    variable: String,
+
+    /// Variable label, if any.
+    #[serde(rename = "@label")]
+    label: Option<String>,
+
+    #[serde(rename = "@categorical")]
+    #[serde(default)] // XXX not sure this is the correct default
+    categorical: bool,
+
+    /// The `id` for a [SourceVariable] whose string values correspond
+    /// one-to-one with the values of this variable and are suitable as value
+    /// labels.
+    #[serde(rename = "@labelVariable")]
+    label_variable: Option<String>,
+
+    format: Option<Format>,
+    string_format: Option<StringFormat>,
+}
+
+impl Variable for SourceVariable {
+    fn decode<'a>(
+        &'a self,
+        locale: &'static Locale<'static>,
+        data: &DataSources,
+        series: &mut BTreeMap<&'a str, Series>,
+        _warn: &mut dyn FnMut(GraphWarning),
+    ) -> bool {
+        let label_series = if let Some(label_variable) = &self.label_variable {
+            if let Some(label_series) = series.get(label_variable.as_str()) {
+                Some(label_series)
+            } else {
+                return false;
+            }
+        } else {
+            None
+        };
+
+        let mut label = self.label.clone();
+        let mut data = if let Some(source) = data.0.get(&self.source)
+            && let Some(values) = source.0.get(&self.variable)
+        {
+            if label.is_none() {
+                label = values.label.clone();
+            }
+            values
+                .data
+                .iter()
+                .map(|value| SeriesValue {
+                    value: value.clone(),
+                    label: None,
+                })
+                .collect::<Vec<_>>()
+        } else {
+            // XXX warn about missing variable
+            Vec::new()
+        };
+        let formatter = self.format.as_ref().map_or_else(
+            || Formatter::default_for_locale(locale),
+            |format| Formatter::new(format, locale),
+        );
+        if let Some(format) = &self.format {
+            format.mapping(&formatter).apply(&mut data);
+        } else if let Some(string_format) = &self.string_format {
+            string_format.mapping().apply(&mut data);
+        };
+        if let Some(label_series) = label_series {
+            for (value, _label) in zip(data.iter_mut(), &label_series.values) {
+                value.label = Some(formatter.display(&value.value).to_string());
+            }
+        }
+        series.insert(
+            &self.id,
+            Series::new(self.id.clone(), data, self.categorical, formatter).with_label(label),
+        );
+        true
+    }
+    fn name(&self) -> &str {
+        &self.variable
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DerivedVariable {
+    #[serde(rename = "@id")]
+    id: String,
+
+    /// An expression that defines the variable's value.
+    #[serde(rename = "@value")]
+    value: String,
+
+    #[serde(rename = "@categorical")]
+    categorical: bool,
+
+    format: Option<Format>,
+    string_format: Option<StringFormat>,
+}
+
+impl Variable for DerivedVariable {
+    fn decode<'a>(
+        &'a self,
+        locale: &'static Locale<'static>,
+        _data: &DataSources,
+        series: &mut BTreeMap<&'a str, Series>,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> bool {
+        let mut values = if self.id == "column" || self.id == "row" {
+            vec![]
+        } else if let Some(rest) = self.id.strip_prefix("dimension")
+            && rest.parse::<usize>().is_ok()
+        {
+            vec![]
+        } else if self.value == "constant(0)" {
+            /// All the series have the same length, except for series with
+            /// length zero.  This returns the length of the first nonempty
+            /// series, or `None` if there isn't one yet.
+            fn series_len(series: &mut BTreeMap<&str, Series>) -> Option<usize> {
+                series.values().find_map(|series| {
+                    if !series.values.is_empty() {
+                        Some(series.values.len())
+                    } else {
+                        None
+                    }
+                })
+            }
+
+            if let Some(n_values) = series_len(series) {
+                vec![
+                    SeriesValue {
+                        value: Datum::Number(Some(0.0)),
+                        label: None,
+                    };
+                    n_values
+                ]
+            } else {
+                return false;
+            }
+        } else if self.value.starts_with("constant") {
+            vec![]
+        } else if let Some(rest) = self.value.strip_prefix("map(")
+            && let Some(var_name) = rest.strip_suffix(")")
+        {
+            let Some(dependency) = series.get(var_name) else {
+                return false;
+            };
+            dependency.values.clone()
+        } else {
+            warn(GraphWarning::UnsupportedValue {
+                variable: self.id.clone(),
+                value: self.value.clone(),
+            });
+            vec![]
+        };
+        let formatter = self.format.as_ref().map_or_else(
+            || Formatter::default_for_locale(locale),
+            |format| Formatter::new(format, locale),
+        );
+        if let Some(format) = &self.format {
+            format.mapping(&formatter).apply(&mut values);
+        } else if let Some(string_format) = &self.string_format {
+            string_format.mapping().apply(&mut values);
+        }
+        series.insert(
+            &self.id,
+            Series::new(self.id.clone(), values, self.categorical, formatter),
+        );
+        true
+    }
+    fn name(&self) -> &str {
+        &self.id
+    }
+}
+
+#[derive(Clone, Debug)]
+struct AxisDomain<'a> {
+    domain: Domain,
+    formatter: Formatter,
+    axis: Option<&'a Axis>,
+    _label: Option<&'a Label>,
+    major_ticks: Option<&'a MajorTicks>,
+}
+
+impl<'a> AxisDomain<'a> {
+    fn default_for_locale(locale: &'static Locale<'static>) -> Self {
+        Self {
+            domain: Domain::default(),
+            formatter: Formatter::default_for_locale(locale),
+            axis: None,
+            _label: None,
+            major_ticks: None,
+        }
+    }
+    fn ticks(&self) -> Vec<(f64, String)> {
+        let mut ticks = Vec::new();
+        if self.major_ticks.is_none() {
+            return ticks;
+        }
+        match &self.domain {
+            Domain::Interval {
+                min: Some(min),
+                max: Some(max),
+                exact: _,
+            } => {
+                let scaling = Scaling::for_range(*min..*max);
+                let formatter = match &self.formatter {
+                    Formatter::Number(number_formatter)
+                        if number_formatter.maximum_fraction_digits.is_none() =>
+                    {
+                        Cow::Owned(Formatter::Number(
+                            number_formatter.clone().with_maximum_fraction_digits(Some(
+                                scaling.maximum_fraction_digits(),
+                            )),
+                        ))
+                    }
+                    _ => Cow::Borrowed(&self.formatter),
+                };
+                for x in scaling.ticks() {
+                    let datum = Datum::Number(Some(x));
+                    if let Some(frac) = self.domain.lookup(&datum) {
+                        ticks.push((frac, formatter.display(&datum).to_string()));
+                    }
+                }
+            }
+            Domain::Categorical { values } => {
+                for (index, (value, label)) in values.iter().enumerate() {
+                    if self.domain.lookup(value).is_some() {
+                        let left = index as f64 / values.len() as f64;
+                        let right = (index + 1) as f64 / values.len() as f64;
+                        let fraction = left.midpoint(right);
+                        ticks.push((
+                            fraction,
+                            label
+                                .clone()
+                                .unwrap_or_else(|| self.formatter.display(value).to_string()),
+                        ));
+                    }
+                }
+            }
+            _ => (),
+        };
+        ticks
+    }
+}
+
+#[derive(Clone, Debug)]
+enum Domain {
+    Categorical {
+        values: IndexMap<Datum<String>, Option<String>>,
+    },
+    Interval {
+        min: Option<f64>,
+        max: Option<f64>,
+        exact: bool,
+    },
+}
+
+impl Default for Domain {
+    fn default() -> Self {
+        Self::new_interval()
+    }
+}
+
+impl Domain {
+    fn new_categorical() -> Self {
+        Self::Categorical {
+            values: IndexMap::new(),
+        }
+    }
+    fn new_interval() -> Self {
+        Self::Interval {
+            min: None,
+            max: None,
+            exact: false,
+        }
+    }
+    fn lookup(&self, value: &Datum<String>) -> Option<f64> {
+        self.lookup_range(value)
+            .map(|range| range.start.midpoint(range.end))
+    }
+    fn lookup_range(&self, value: &Datum<String>) -> Option<Range<f64>> {
+        match self {
+            Domain::Categorical { values } => {
+                if let Some(index) = values.get_index_of(value) {
+                    let left = index as f64 / values.len() as f64;
+                    let right = (index + 1) as f64 / values.len() as f64;
+                    Some(left..right)
+                } else {
+                    None
+                }
+            }
+            Domain::Interval {
+                min: Some(min),
+                max: Some(max),
+                exact: _,
+            } => {
+                if let Some(Some(number)) = value.as_number()
+                    && (*min..=*max).contains(&number)
+                {
+                    let value = (number - *min) / (*max - *min);
+                    Some(value..value)
+                } else {
+                    None
+                }
+            }
+            _ => None,
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CategoricalDomain {
+    #[serde(rename = "@id")]
+    id: String,
+
+    #[serde(default, rename = "@date")]
+    _date: bool,
+
+    #[serde(default, rename = "$value")]
+    children: Vec<CategoricalDomainChild>,
+}
+
+impl RawDomain for CategoricalDomain {
+    fn decode<'a>(
+        &'a self,
+        series: &BTreeMap<&str, Series>,
+        domains: &mut BTreeMap<&'a str, Domain>,
+    ) -> bool {
+        fn add_value(
+            values: &mut IndexMap<Datum<String>, Option<String>>,
+            value: Datum<String>,
+            label: Option<String>,
+        ) {
+            let entry_label = values.entry(value).or_default();
+            if label.is_some() {
+                *entry_label = label;
+            }
+        }
+
+        let mut values: IndexMap<Datum<String>, Option<String>> = IndexMap::new();
+        for child in &self.children {
+            match child {
+                CategoricalDomainChild::VariableReference(variable_reference) => {
+                    if let Some(series) = series.get(variable_reference.reference.as_str()) {
+                        assert!(series.categorical);
+                        for sv in &series.values {
+                            add_value(&mut values, sv.value.clone(), sv.label.clone());
+                        }
+                    } else {
+                        todo!()
+                    }
+                }
+                CategoricalDomainChild::DomainReference(domain) => {
+                    let Some(domain) = domains.get(domain.reference.as_str()) else {
+                        return false;
+                    };
+
+                    match domain {
+                        Domain::Categorical { values: inner } => {
+                            for (label, value) in inner {
+                                add_value(&mut values, label.clone(), value.clone());
+                            }
+                        }
+                        Domain::Interval { .. } => todo!(),
+                    }
+                }
+                CategoricalDomainChild::Categories(categories) => {
+                    for category in categories.categories.split(';') {
+                        let datum = if let Ok(number) = category.parse() {
+                            Datum::Number(Some(number))
+                        } else {
+                            Datum::String(category.into())
+                        };
+                        if categories.remove {
+                            values.shift_remove(&datum);
+                        } else {
+                            add_value(&mut values, datum, None);
+                        }
+                    }
+                }
+                CategoricalDomainChild::SimpleSort(sort) => {
+                    sort.sort(&mut values);
+                }
+                CategoricalDomainChild::StatisticSort => dbg!(),
+            }
+        }
+        domains.insert(self.id.as_str(), Domain::Categorical { values });
+        true
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum CategoricalDomainChild {
+    VariableReference(VariableReference),
+    DomainReference(DomainReference),
+    Categories(Categories),
+    SimpleSort(SimpleSort),
+    StatisticSort,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SimpleSort {
+    #[serde(rename = "@method")]
+    method: SortMethod,
+
+    category_order: Option<CategoryOrder>,
+}
+
+impl SimpleSort {
+    fn sort(&self, values: &mut IndexMap<Datum<String>, Option<String>>) {
+        match self.method {
+            SortMethod::Data => (),
+            SortMethod::Custom => {
+                if let Some(order) = &self.category_order {
+                    let mut categories = IndexSet::new();
+                    for category in order.order.split(';') {
+                        let datum = if let Ok(number) = category.parse() {
+                            Datum::Number(Some(number))
+                        } else {
+                            Datum::String(category.into())
+                        };
+                        categories.insert(datum);
+                    }
+                    values.sort_by_key(|k, _v| categories.get_index_of(k).unwrap_or(usize::MAX));
+                }
+            }
+            SortMethod::Natural => values.sort_unstable_keys(),
+            SortMethod::NaturalUsingLabel => values.sort_by(|_ak, av, _bk, bv| av.cmp(bv)),
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct CategoryOrder {
+    #[serde(rename = "$value")]
+    order: String,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum SortMethod {
+    Data,
+    Custom,
+    Natural,
+    NaturalUsingLabel,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Categories {
+    #[serde(rename = "@remove")]
+    remove: bool,
+
+    #[serde(default, rename = "$text")]
+    categories: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct IntervalDomain {
+    #[serde(rename = "@id")]
+    id: String,
+
+    #[serde(default, rename = "@date")]
+    _date: bool,
+
+    #[serde(default, rename = "$value")]
+    children: Vec<IntervalDomainChild>,
+}
+
+impl RawDomain for IntervalDomain {
+    fn decode<'a>(
+        &'a self,
+        series: &BTreeMap<&str, Series>,
+        domains: &mut BTreeMap<&'a str, Domain>,
+    ) -> bool {
+        let ranges = self.children.iter().flat_map(|child| child.range());
+        let mut min = ranges
+            .clone()
+            .flat_map(|range| range.min)
+            .min_by(|x, y| x.total_cmp(&y));
+        let mut max = ranges
+            .clone()
+            .flat_map(|range| range.max)
+            .max_by(|x, y| x.total_cmp(&y));
+
+        let exact = ranges
+            .clone()
+            .find_map(|range| range.exact)
+            .unwrap_or_default();
+        for variable in self.children.iter().flat_map(|child| child.variable()) {
+            if let Some(series) = series.get(variable.reference.as_str()) {
+                for sv in &series.values {
+                    if let Datum::Number(Some(number)) = &sv.value {
+                        let number = *number;
+                        if min.is_none_or(|min| number < min) {
+                            min = Some(number);
+                        }
+                        if max.is_none_or(|max| number > max) {
+                            max = Some(number);
+                        }
+                    }
+                }
+            } else {
+                todo!()
+            }
+        }
+        domains.insert(self.id.as_str(), Domain::Interval { min, max, exact });
+        true
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum IntervalDomainChild {
+    Range(IntervalRange),
+    VariableReference(VariableReference),
+    Format(#[allow(dead_code)] Format),
+}
+
+impl IntervalDomainChild {
+    fn range(&self) -> Option<&IntervalRange> {
+        match self {
+            IntervalDomainChild::Range(interval_range) => Some(interval_range),
+            _ => None,
+        }
+    }
+    fn variable(&self) -> Option<&VariableReference> {
+        match self {
+            IntervalDomainChild::VariableReference(reference) => Some(reference),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct IntervalRange {
+    #[serde(rename = "@min")]
+    min: Option<f64>,
+
+    #[serde(rename = "@max")]
+    max: Option<f64>,
+
+    #[serde(rename = "@exact")]
+    exact: Option<bool>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct DomainReference {
+    #[serde(rename = "@ref")]
+    reference: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct VariableReference {
+    #[serde(rename = "@ref")]
+    reference: String,
+}
+
+impl VariableReference {
+    fn decode_faceting<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        axis_index: usize,
+        variable_index: &mut usize,
+    ) {
+        let axis = if axis_index % 2 == 0 {
+            Axis2::X
+        } else {
+            Axis2::Y
+        };
+        *variable_index += 1;
+        let series = series.get(self.reference.as_str()).unwrap(); // XXX
+        assert!(series.categorical); // XXX
+        faceting[axis].push(Facet {
+            series,
+            facet_level: facet_levels.get(&*variable_index).map(|v| &**v),
+        });
+    }
+
+    fn expression(&self) -> Expression {
+        Expression::new_variable(&self.reference)
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct StringFormat {
+    #[serde(default, rename = "relabel")]
+    relabels: Vec<Relabel>,
+    #[serde(default, rename = "affix")]
+    _affixes: Vec<Affix>,
+}
+
+impl StringFormat {
+    fn mapping(&self) -> Map {
+        Map(self
+            .relabels
+            .iter()
+            .map(|relabel| (relabel.from(), relabel.to.clone()))
+            .collect())
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Format {
+    #[serde(rename = "@tryStringsAsNumbers")]
+    _try_strings_as_numbers: Option<bool>,
+    #[serde(rename = "@relabelsArePreformatted")]
+    _relabels_are_preformatted: Option<bool>,
+    #[serde(rename = "@separatorChars")]
+    separator_chars: Option<String>,
+    #[serde(rename = "@hiddenseparator")]
+    #[serde(default)]
+    hidden_separator: bool,
+    #[serde(default, rename = "@prefix")]
+    prefix: String,
+    #[serde(default, rename = "@suffix")]
+    suffix: String,
+
+    #[serde(rename = "@baseFormat")]
+    base_format: Option<BaseFormat>,
+    #[serde(rename = "@scientific")]
+    scientific: Option<RawScientific>,
+    #[serde(rename = "@small")]
+    small: Option<f64>,
+    #[serde(rename = "@errorCharacter")]
+    error_character: Option<char>,
+    #[serde(rename = "@minimumFractionDigits")]
+    minimum_fraction_digits: Option<usize>,
+    #[serde(rename = "@maximumFractionDigits")]
+    maximum_fraction_digits: Option<usize>,
+    #[serde(rename = "@minimumIntegerDigits")]
+    minimum_integer_digits: Option<usize>,
+    #[serde(rename = "@maximumIntegerDigits")]
+    maximum_integer_digits: Option<usize>,
+    #[serde(rename = "@useGrouping")]
+    use_grouping: Option<bool>,
+    #[serde(rename = "@hideNegatives")]
+    #[serde(default)]
+    hide_negatives: bool,
+    #[serde(rename = "@negativesOutside")]
+    #[serde(default)]
+    negatives_outside: bool,
+    #[serde(rename = "@scalingFactor")]
+    scaling_factor: Option<f64>,
+    #[serde(rename = "@negativesInParentheses")]
+    #[serde(default)]
+    negatives_in_parentheses: bool,
+    #[serde(rename = "@mdyOrder")]
+    mdy_order: Option<MdyOrder>,
+    #[serde(rename = "@dayOfMonthPadding")]
+    _day_of_month_padding: Option<bool>,
+    #[serde(rename = "@weekPadding")]
+    week_padding: Option<bool>,
+    #[serde(rename = "@dayPadding")]
+    day_padding: Option<bool>,
+    #[serde(rename = "@hourPadding")]
+    hour_padding: Option<bool>,
+    #[serde(rename = "@minutePadding")]
+    minute_padding: Option<bool>,
+    #[serde(rename = "@secondPadding")]
+    second_padding: Option<bool>,
+    #[serde(rename = "@showEra")]
+    show_era: Option<bool>,
+    #[serde(rename = "@showYear")]
+    show_year: Option<bool>,
+    #[serde(rename = "@showQuarter")]
+    show_quarter: Option<bool>,
+    #[serde(rename = "@showMonth")]
+    show_month: Option<bool>,
+    #[serde(rename = "@showWeek")]
+    show_week: Option<bool>,
+    #[serde(rename = "@showDay")]
+    show_day: Option<bool>,
+    #[serde(rename = "@showHour")]
+    show_hour: Option<bool>,
+    #[serde(rename = "@showMinute")]
+    show_minute: Option<bool>,
+    #[serde(rename = "@showSecond")]
+    show_second: Option<bool>,
+    #[serde(rename = "@showMillis")]
+    show_millis: Option<bool>,
+    #[serde(rename = "@dayOfWeekAbbreviation")]
+    day_of_week_abbreviation: Option<bool>,
+    #[serde(rename = "@yearAbbreviation")]
+    year_abbreviation: Option<bool>,
+    #[serde(rename = "@quarterPrefix")]
+    #[serde(default)]
+    quarter_prefix: String,
+    #[serde(rename = "@quarterSuffix")]
+    #[serde(default)]
+    quarter_suffix: String,
+    #[serde(rename = "@weekSuffix")]
+    #[serde(default)]
+    week_suffix: String,
+    #[serde(rename = "@dayType")]
+    day_type: Option<DayType>,
+    #[serde(rename = "@hourFormat")]
+    hour_format: Option<HourFormat>,
+    #[serde(rename = "@monthFormat")]
+    month_format: Option<MonthFormat>,
+    #[serde(rename = "@showDayOfWeek")]
+    show_day_of_week: Option<bool>,
+
+    #[serde(default, rename = "relabel")]
+    relabels: Vec<Relabel>,
+    #[serde(default, rename = "affix")]
+    _affixes: Vec<Affix>,
+}
+
+impl Format {
+    fn mapping(&self, formatter: &Formatter) -> Map {
+        Map(self
+            .relabels
+            .iter()
+            .map(|relabel| {
+                let value = if let Some(to) = relabel.to.trim().parse::<f64>().ok() {
+                    formatter
+                        .display(&Datum::<String>::Number(Some(to)))
+                        .to_string()
+                } else {
+                    relabel.to.clone()
+                };
+                (relabel.from(), value)
+            })
+            .collect())
+    }
+
+    fn nth_separator(&self, n: usize) -> Option<char> {
+        if let Some(separator_chars) = &self.separator_chars {
+            separator_chars.chars().nth(n)
+        } else {
+            None
+        }
+    }
+
+    fn decimal(&self, locale: &Locale) -> char {
+        self.nth_separator(0).unwrap_or_else(|| locale.decimal())
+    }
+
+    fn group(&self, locale: &Locale) -> char {
+        self.nth_separator(1).unwrap_or_else(|| locale.group())
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum BaseFormat {
+    Date,
+    Time,
+    DateTime,
+    ElapsedTime,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum MdyOrder {
+    DayMonthYear,
+    DayYearMonth,
+    MonthDayYear,
+    MonthYearDay,
+    YearMonthDay,
+    YearDayMonth,
+}
+
+impl IntoIterator for MdyOrder {
+    type Item = Mdy;
+
+    type IntoIter = std::array::IntoIter<Mdy, 3>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        use Mdy::*;
+        match self {
+            MdyOrder::DayMonthYear => [Day, Month, Year],
+            MdyOrder::DayYearMonth => [Day, Year, Month],
+            MdyOrder::MonthDayYear => [Month, Day, Year],
+            MdyOrder::MonthYearDay => [Month, Year, Day],
+            MdyOrder::YearMonthDay => [Year, Month, Day],
+            MdyOrder::YearDayMonth => [Year, Day, Month],
+        }
+        .into_iter()
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+enum Mdy {
+    Month,
+    Day,
+    Year,
+}
+
+struct NotMdy;
+
+impl TryFrom<&Item<'_>> for Mdy {
+    type Error = NotMdy;
+
+    fn try_from(value: &Item<'_>) -> Result<Self, Self::Error> {
+        if value.is_year() {
+            Ok(Self::Year)
+        } else if value.is_month() {
+            Ok(Self::Month)
+        } else if value.is_day() {
+            Ok(Self::Day)
+        } else {
+            Err(NotMdy)
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum DayType {
+    /// Day of the month.
+    Month,
+    /// Day of the year.
+    Year,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum HourFormat {
+    /// Use AM/PM suffix.
+    #[serde(rename = "AMPM")]
+    AmPm,
+    /// Use 24-hour format.
+    #[serde(rename = "AS_24")]
+    As24,
+    /// Use 12-hour format (ambiguous as to AM/PM).
+    #[serde(rename = "AS_12")]
+    As12,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum MonthFormat {
+    /// January
+    Long,
+    /// Jan
+    Short,
+    /// 1
+    Number,
+    /// 01
+    PaddedNumber,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum RawScientific {
+    OnlyForSmall,
+    WhenNeeded,
+    True,
+    False,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Affix {
+    /// A reference to a footnote with the given 1-based index.
+    #[serde(rename = "@definesReference")]
+    _defines_reference: NonZeroUsize,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Relabel {
+    #[serde(rename = "@from")]
+    from: String,
+    #[serde(rename = "@to")]
+    to: String,
+}
+
+impl Relabel {
+    fn from(&self) -> Datum<String> {
+        match self.from.parse() {
+            Ok(number) => Datum::Number(Some(number)),
+            Err(_) => Datum::String(self.from.clone()),
+        }
+    }
+}
+
+struct Element<'a> {
+    raw: &'a RawElement,
+    style: Cow<'a, Style>,
+    variables: EnumMap<Axis3, Option<Series>>,
+
+    /// Whether to ignore this element for the purpose of computing minimum and
+    /// maximum ordinates on an axis.
+    ///
+    /// This is set to true for statistics that produce data for an axis but
+    /// shouldn't expand the axis range, e.g. probability distribution
+    /// functions.
+    ignore_minmax: EnumMap<Axis3, bool>,
+}
+
+impl<'a> Element<'a> {
+    fn new(
+        raw: &'a RawElement,
+        locale: &'static Locale<'static>,
+        series: &BTreeMap<&str, Series>,
+        bin_widths: &mut BTreeMap<String, f64>,
+        axis_domains: &mut EnumMap<Axis3, AxisDomain<'a>>,
+        styles: &'a HashMap<String, Style>,
+    ) -> Result<Self, GraphError> {
+        let mut variables = EnumMap::try_from_fn(|axis| {
+            raw.positions[axis]
+                .as_ref()
+                .map(|coordinate| {
+                    series
+                        .get(coordinate.variable.as_str())
+                        .cloned()
+                        .ok_or_else(|| GraphError::MissingVariable(coordinate.variable.clone()))
+                })
+                .transpose()
+        })?;
+        let mut ignore_minmax = EnumMap::default();
+        for statistic in &raw.statistics {
+            match statistic {
+                Statistic::BinStatistic(bin_statistic) => {
+                    if let Some(x) = &mut variables[Axis3::X] {
+                        assert!(!x.categorical);
+                        let mut min = None;
+                        let mut max = None;
+                        let mut n = 0;
+                        for value in &x.values {
+                            if let Datum::Number(Some(number)) = &value.value {
+                                if min.is_none_or(|min| number < min) {
+                                    min = Some(number);
+                                }
+                                if max.is_none_or(|max| number > max) {
+                                    max = Some(number);
+                                }
+                                n += 1;
+                            }
+                        }
+                        if let Some(min) = min.copied()
+                            && let Some(max) = max.copied()
+                            && max > min
+                        {
+                            let mut binning =
+                                Binning::for_range(min..max, sturges_rule(n as f64).unwrap_or(10));
+                            if let Some(bin_width) = bin_statistic.bin_width.first().copied()
+                                && let Some(bin_start) = bin_statistic.bin_start.first().copied()
+                            {
+                                binning.width = bin_width;
+                                binning.start = bin_start;
+                                binning.n = NumCast::from((max - bin_start) / bin_width)
+                                    .unwrap_or(usize::MAX);
+                            }
+                            let bin_width =
+                                bin_widths.entry(x.name.clone()).or_insert(binning.width);
+                            *bin_width = bin_width.min(binning.width);
+                            for value in &mut x.values {
+                                if let Datum::Number(Some(number)) = &value.value {
+                                    value.value = Datum::Number(binning.bin(*number));
+                                }
+                                // XXX set value.label
+                            }
+                        }
+                        x.categorical = true;
+                    }
+                }
+                Statistic::SummaryStatistic(summary_statistic) => {
+                    if let Some(mut x) = variables[Axis3::X].take() {
+                        let mut frequencies = BTreeMap::<Datum<String>, f64>::new();
+                        assert!(x.categorical);
+                        for value in &x.values {
+                            if !value.value.is_sysmis() {
+                                *frequencies.entry(value.value.clone()).or_default() += 1.0;
+                            }
+                        }
+
+                        if summary_statistic.convert_interval_to_single_value {
+                            x.values = frequencies
+                                .keys()
+                                .map(|value| SeriesValue {
+                                    value: value.clone(),
+                                    label: None,
+                                })
+                                .collect();
+                        };
+
+                        let total = frequencies.values().sum::<f64>();
+                        let mut y = x.clone();
+                        y.categorical = false;
+                        y.formatter = Formatter::default_for_locale(locale);
+                        for value in &mut y.values {
+                            let frequency =
+                                frequencies.get(&value.value).copied().unwrap_or_default();
+                            let result = match summary_statistic.method {
+                                SummaryMethod::Mean => todo!(),
+                                SummaryMethod::Sum => todo!(),
+                                SummaryMethod::MidRange => todo!(),
+                                SummaryMethod::PercentCount => {
+                                    if total != 0.0 {
+                                        Some(frequency / total * 100.0)
+                                    } else {
+                                        None
+                                    }
+                                }
+                                SummaryMethod::Percent => todo!(),
+                                SummaryMethod::Fraction => todo!(),
+                                SummaryMethod::FractionCount => todo!(),
+                                SummaryMethod::Range => todo!(),
+                                SummaryMethod::ConfidenceIntervalOfMean => todo!(),
+                                SummaryMethod::ConfidenceIntervalIndividual => todo!(),
+                                SummaryMethod::Count => Some(frequency),
+                                SummaryMethod::ConfidenceIntervalOfCount => todo!(),
+                                SummaryMethod::ConfidenceIntervalOfPercentCount => todo!(),
+                                SummaryMethod::ConfidenceIntervalOfFractionCount => todo!(),
+                                SummaryMethod::RowCount => todo!(),
+                                SummaryMethod::Stddev => todo!(),
+                                SummaryMethod::Stderr => todo!(),
+                                SummaryMethod::Median => todo!(),
+                                SummaryMethod::Max => todo!(),
+                                SummaryMethod::Min => todo!(),
+                                SummaryMethod::Variance => todo!(),
+                                SummaryMethod::CumulativeSum => todo!(),
+                                SummaryMethod::CumulativePercent => todo!(),
+                                SummaryMethod::CumulativePercentCount => todo!(),
+                                SummaryMethod::CumulativeFraction => todo!(),
+                                SummaryMethod::CumulativeFractionCount => todo!(),
+                                SummaryMethod::First => todo!(),
+                                SummaryMethod::Last => todo!(),
+                                SummaryMethod::Percentile => todo!(),
+                                SummaryMethod::Mode => todo!(),
+                                SummaryMethod::CountTrue => todo!(),
+                                SummaryMethod::PercentTrue => todo!(),
+                                SummaryMethod::FractionTrue => todo!(),
+                            };
+                            value.value = Datum::Number(result);
+                        }
+                        variables[Axis3::X] = Some(x);
+                        variables[Axis3::Y] = Some(y);
+                    }
+                }
+                Statistic::DistributionStatistic(_) => {
+                    if let Some(x) = &variables[Axis3::X]
+                        && let Domain::Interval {
+                            min: Some(x_min),
+                            max: Some(x_max),
+                            ..
+                        } = &axis_domains[Axis3::X].domain
+                    {
+                        let mut sum = 0.0;
+                        let mut sum2 = 0.0;
+                        let mut n = 0.0;
+                        for value in &x.values {
+                            if let Datum::Number(Some(number)) = &value.value {
+                                sum += *number;
+                                sum2 += number.powi(2);
+                                n += 1.0;
+                            }
+                        }
+                        if n > 1.0 {
+                            let mean = sum / n;
+                            let stddev = ((n * sum2 - sum.powi(2)) / (n * (n - 1.0))).sqrt();
+                            let bin_width = bin_widths.get(&x.name).copied().unwrap_or(1.0);
+                            let mut series_x = x.clone();
+                            series_x.values = Vec::with_capacity(100);
+                            let mut series_y = x.clone();
+                            series_y.values = Vec::with_capacity(100);
+                            for step in 0..=100 {
+                                let x = (*x_max - *x_min) * (step as f64 / 100.0) + *x_min;
+                                let y = normal_pdf(x, mean, stddev).unwrap() * n * bin_width;
+                                series_x.values.push(x.into());
+                                series_y.values.push(y.into());
+                            }
+                            variables[Axis3::X] = Some(series_x);
+                            variables[Axis3::Y] = Some(series_y);
+                            ignore_minmax[Axis3::X] = true;
+                        }
+                    }
+                }
+                Statistic::TukeyStatistic => {
+                    if let Some(x) = &variables[Axis3::X]
+                        && let Some(y) = &variables[Axis3::Y]
+                    {
+                        let mut values = BTreeMap::<_, Vec<_>>::new();
+                        for (x, y) in x.values.iter().zip(&y.values) {
+                            if let Datum::Number(Some(number)) = &y.value {
+                                values.entry(x.value.clone()).or_default().push(*number);
+                            }
+                        }
+                        let mut x_results = Vec::new();
+                        let mut y_results = Vec::new();
+                        for (x, numbers) in &mut values {
+                            numbers.sort_by_key(|number| OrderedFloat(*number));
+                            if numbers.len() >= 2 {
+                                fn calculate_median(numbers: &[f64]) -> f64 {
+                                    let len = numbers.len();
+                                    if len.is_odd() {
+                                        numbers[len / 2]
+                                    } else {
+                                        numbers[len / 2 - 1].midpoint(numbers[len / 2])
+                                    }
+                                }
+                                let median = calculate_median(&numbers);
+                                let lower_hinge =
+                                    calculate_median(&numbers[..numbers.len().div_ceil(2)]);
+                                let upper_hinge = calculate_median(&numbers[numbers.len() / 2..]);
+                                let spread = upper_hinge - lower_hinge;
+                                let lower_fence_limit = lower_hinge - 1.5 * spread;
+                                let mut lower_fence = median;
+                                let upper_fence_limit = upper_hinge + 1.5 * spread;
+                                let mut upper_fence = median;
+                                for number in &*numbers {
+                                    if *number < lower_fence && *number >= lower_fence_limit {
+                                        lower_fence = *number;
+                                    }
+                                    if *number <= upper_fence_limit {
+                                        upper_fence = *number;
+                                    }
+                                }
+                                y_results.extend(
+                                    [median, lower_hinge, upper_hinge, lower_fence, upper_fence]
+                                        .into_iter()
+                                        .chain(numbers.iter().copied().filter(|number| {
+                                            *number < lower_fence || *number > upper_fence
+                                        }))
+                                        .map(|number| SeriesValue {
+                                            value: Datum::from(number),
+                                            label: None,
+                                        }),
+                                );
+                                x_results.extend((x_results.len()..y_results.len()).map(|_| {
+                                    SeriesValue {
+                                        value: x.clone(),
+                                        label: None,
+                                    }
+                                }));
+                            } else {
+                                // XXX
+                            }
+                        }
+                        variables[Axis3::X].as_mut().unwrap().values = x_results;
+                        variables[Axis3::Y].as_mut().unwrap().values = y_results;
+                    }
+                }
+                Statistic::Other => (),
+            }
+        }
+        Ok(Self {
+            raw,
+            style: if let Some(style) = raw.style.as_deref()
+                && let Some(style) = styles.get(style)
+            {
+                Cow::Borrowed(style)
+            } else {
+                Cow::Owned(Style::default())
+            },
+            variables,
+            ignore_minmax,
+        })
+    }
+}
+
+#[derive(Debug)]
+struct Graph {
+    id: Option<String>,
+    cell_style: Option<String>,
+    locations: Vec<Location>,
+    faceting: Option<Faceting>,
+    facet_layout: Option<FacetLayout>,
+    coordinates: Coordinates,
+    elements: Vec<RawElement>,
+}
+
+impl Graph {
+    fn facet_levels(&self) -> HashMap<usize, &FacetLevel> {
+        self.facet_layout
+            .iter()
+            .flat_map(|facet_layout| facet_layout.facet_levels())
+            .map(|facet_level| (facet_level.level, facet_level))
+            .collect()
+    }
+
+    fn axis_dimension(&self, axis: Axis3) -> Option<&Dimension> {
+        self.coordinates.dimensions[axis].as_ref()
+    }
+}
+
+impl<'de> Deserialize<'de> for Graph {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        struct RawGraph {
+            #[serde(rename = "@id")]
+            id: Option<String>,
+
+            #[serde(rename = "@cellStyle")]
+            cell_style: Option<String>,
+
+            #[serde(rename = "$value")]
+            children: Vec<GraphChild>,
+        }
+
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        enum GraphChild {
+            Location(Location),
+            Faceting(Faceting),
+            FacetLayout(FacetLayout),
+            Coordinates(Coordinates),
+            Interval(RawElement),
+            Point(RawElement),
+            Line(RawElement),
+            Polygon(RawElement),
+            Area(RawElement),
+            Schema(RawElement),
+            Edge(RawElement),
+            Path(RawElement),
+            LineGuide,
+        }
+
+        let raw_graph = RawGraph::deserialize(deserializer)?;
+        let mut locations = Vec::new();
+        let mut faceting = None;
+        let mut facet_layout = None;
+        let mut coordinates = None;
+        let mut elements = Vec::new();
+        for child in raw_graph.children {
+            match child {
+                GraphChild::Interval(interval) => {
+                    elements.push(interval.with_graphic(Graphic::Interval))
+                }
+                GraphChild::Point(point) => elements.push(point.with_graphic(Graphic::Point)),
+                GraphChild::Line(line) => elements.push(line.with_graphic(Graphic::Line)),
+                GraphChild::Polygon(polygon) => {
+                    elements.push(polygon.with_graphic(Graphic::Polygon))
+                }
+                GraphChild::Area(area) => elements.push(area.with_graphic(Graphic::Area)),
+                GraphChild::Schema(schema) => elements.push(schema.with_graphic(Graphic::Schema)),
+                GraphChild::Edge(edge) => elements.push(edge.with_graphic(Graphic::Edge)),
+                GraphChild::Path(path) => elements.push(path.with_graphic(Graphic::Path)),
+                GraphChild::Location(location) => locations.push(location),
+                GraphChild::Faceting(value) => {
+                    if faceting.replace(value).is_some() {
+                        return Err(D::Error::duplicate_field("faceting"));
+                    }
+                }
+                GraphChild::FacetLayout(value) => {
+                    if facet_layout.replace(value).is_some() {
+                        return Err(D::Error::duplicate_field("facetLayout"));
+                    }
+                }
+                GraphChild::Coordinates(value) => {
+                    if coordinates.replace(value).is_some() {
+                        return Err(D::Error::duplicate_field("coordinates"));
+                    }
+                }
+                GraphChild::LineGuide => (),
+            };
+        }
+        Ok(Self {
+            id: raw_graph.id,
+            cell_style: raw_graph.cell_style,
+            locations,
+            faceting,
+            facet_layout,
+            coordinates: coordinates.ok_or_else(|| D::Error::missing_field("coordinates"))?,
+            elements,
+        })
+    }
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum BreakRepresentation {
+    Gap,
+    Interpolate,
+    Wings,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum Graphic {
+    /// Bar chart of bars.  Each `(x,y)` bar covers `0..y` at the given `x`
+    /// coordinate.
+    Interval,
+    /// Scatter-plot of `(x,y)` points.
+    Point,
+    /// A graph of connected `(x,y)` points, where it is presumed that `y =
+    /// f(x)` for some function `f`.  For this reason, a line may only have a
+    /// single `y` value for any given `x`.
+    Line,
+    /// A graph of connected `(x,y)` points, where it is assumed that `x` and
+    /// `y` are parameteric in terms of `t`, that is, that its values are
+    /// `(x(t), y(t))`.  Thus, unlike a line, a path may take any shape.
+    Path,
+    /// The area between the x-axis and the `(x,y)` points.
+    ///
+    /// Whereas `interval` represents a collection of intervals, one per bar,
+    /// `area` represents a single area without gaps.
+    Area,
+    /// A boxplot.  All the `y` points for a given `x` represent one boxplot:
+    ///
+    /// - y[0]: median
+    /// - y[1]: lower hinge (median of the lower half)
+    /// - y[2]: upper hinge (median of the upper half)
+    /// - y[3]: lower fence (lower_hinge - 1.5*(upper_hinge - lower_hinge))
+    /// - y[4]: upper fence (upper_hinge + 1.5*(upper_hinge - lower_hinge))
+    /// - y[5..]: values outside the range [lower_fence, upper_fence]
+    Schema,
+    Edge,
+    Polygon,
+}
+
+#[derive(Debug)]
+struct RawElement {
+    id: Option<String>,
+    _break_representation: Option<BreakRepresentation>,
+    _closed: bool,
+    _preserve_straight_lines: bool,
+    _center_in_3d: bool,
+    _median_style: Option<String>,
+    _fence_style: Option<String>,
+    _hinge_style: Option<String>,
+    _outlier_style: Option<String>,
+    _extreme_style: Option<String>,
+    _position_modifier: PositionModifier,
+    style: Option<String>,
+    statistics: Vec<Statistic>,
+
+    /// How to combine the `positions`, either [Operator::Cross] or
+    /// [Operator::Dot].
+    operator: Operator,
+
+    positions: EnumMap<Axis3, Option<Coordinate>>,
+    color: Vec<ColorAesthetic>,
+    style_by: Vec<StyleBy>,
+
+    /// Type of graphic.
+    graphic: Graphic,
+}
+
+impl RawElement {
+    fn with_graphic(mut self, graphic: Graphic) -> Self {
+        self.graphic = graphic;
+        self
+    }
+
+    fn position_expression(&self) -> Expression {
+        let mut positions = self
+            .positions
+            .values()
+            .map(|coord| {
+                coord
+                    .as_ref()
+                    .map_or_default(|coord| Expression::new_variable(&coord.variable))
+            })
+            .collect_vec();
+
+        while positions.pop_if(|e| e.is_unity()).is_some() {}
+
+        self.operator.apply(positions)
+    }
+
+    fn var_set(
+        &self,
+        faceting: Option<&Faceting>,
+        series: &BTreeMap<&str, Series>,
+        warn: &mut dyn FnMut(GraphWarning),
+    ) -> Result<VarSet, VarSetError> {
+        let position_var_set = self.position_expression().to_var_set(series)?;
+        if let Some(faceting) = faceting {
+            let facet_var_set = faceting.expression(warn).to_var_set(series)?;
+            Ok(VarSet::for_operation(
+                faceting.method.into(),
+                position_var_set,
+                facet_var_set,
+            ))
+        } else {
+            Ok(position_var_set)
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for RawElement {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug, Default)]
+        #[serde(rename_all = "camelCase")]
+        #[serde(default)]
+        struct ElementSpec {
+            #[serde(rename = "@id")]
+            id: Option<String>,
+
+            #[serde(rename = "@breakRepresentation")]
+            break_representation: Option<BreakRepresentation>,
+
+            #[serde(rename = "@closed")]
+            closed: bool,
+
+            #[serde(rename = "@preserveStraightLines")]
+            preserve_straight_lines: bool,
+
+            #[serde(rename = "@centerIn3D")]
+            center_in_3d: bool,
+
+            #[serde(rename = "@medianStyle")]
+            median_style: Option<String>,
+
+            #[serde(rename = "@fenceStyle")]
+            fence_style: Option<String>,
+
+            #[serde(rename = "@hingeStyle")]
+            hinge_style: Option<String>,
+
+            #[serde(rename = "@outlierStyle")]
+            outlier_style: Option<String>,
+
+            #[serde(rename = "@extremeStyle")]
+            extreme_style: Option<String>,
+
+            #[serde(rename = "@style")]
+            style: Option<String>,
+
+            #[serde(rename = "@clip")]
+            clip: Option<Clip>,
+
+            #[serde(rename = "@dot")]
+            dot: Option<bool>,
+
+            #[serde(rename = "$value")]
+            statistics: Vec<Statistic>,
+
+            #[serde(rename = "@positionModifier")]
+            position_modifier: PositionModifier,
+
+            /// `position` is just a generic way to say x, y, z
+            position: Vec<Coordinate>,
+            x: Option<Coordinate>,
+            y: Option<Coordinate>,
+            z: Option<Coordinate>,
+            color: Vec<ColorAesthetic>,
+            style_by: Vec<StyleBy>,
+        }
+
+        let raw = ElementSpec::deserialize(deserializer)?;
+        let operator = if raw.position.is_empty() && raw.dot.unwrap_or(true) {
+            Operator::Dot
+        } else {
+            Operator::Cross
+        };
+        let mut positions = EnumMap::from_array([raw.x, raw.y, raw.z]);
+        for (position, axis) in zip(raw.position, all()) {
+            positions[axis] = Some(position);
+        }
+        Ok(Self {
+            id: raw.id,
+            _break_representation: raw.break_representation,
+            _closed: raw.closed,
+            _preserve_straight_lines: raw.preserve_straight_lines,
+            _center_in_3d: raw.center_in_3d,
+            _median_style: raw.median_style,
+            _fence_style: raw.fence_style,
+            _hinge_style: raw.hinge_style,
+            _outlier_style: raw.outlier_style,
+            _extreme_style: raw.extreme_style,
+            _position_modifier: raw.position_modifier,
+            style: raw.style,
+            statistics: raw.statistics,
+            operator,
+            positions,
+            color: raw.color,
+            style_by: raw.style_by,
+            graphic: Graphic::Area,
+        })
+    }
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum PositionModifier {
+    #[default]
+    None,
+    Center,
+    CenterStacked,
+    Stack,
+    StackInterpolating,
+    SetMissingsToZero,
+    MinusIntersection,
+    Dodge,
+    Pile,
+    JitterJointUniform,
+    JitterConditionalUniform,
+    JitterJointNormal,
+    JitterConditionalNormal,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct ColorAesthetic {
+    #[serde(rename = "@id")]
+    _id: Option<String>,
+    #[serde(rename = "@domainModifier")]
+    _domain_modifier: Option<String>,
+    #[serde(rename = "@variable")]
+    variable: Option<String>,
+    #[serde(rename = "@dimension")]
+    _dimension: Option<usize>,
+    #[serde(rename = "@domain")]
+    _domain: Option<String>,
+    #[serde(rename = "@clamp")]
+    _clamp: Option<bool>,
+    #[serde(default, rename = "@split")]
+    _split: bool,
+    //summary_statistic: Option<SummaryStatistic>,
+    #[serde(default, rename = "colorMapping")]
+    _color_mapping: Vec<ColorMapping>,
+
+    #[serde(rename = "@scale")]
+    _scale: Option<ColorScale>,
+
+    #[serde(rename = "@affect")]
+    affect: Option<ColorAffect>,
+
+    #[serde(rename = "@cycle")]
+    _cycle: Option<String>,
+
+    #[serde(rename = "@missing")]
+    _missing: Option<String>,
+
+    #[serde(rename = "@low")]
+    _low: Option<String>,
+
+    #[serde(rename = "@high")]
+    _high: Option<String>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct StyleBy {
+    #[serde(rename = "@variable")]
+    variable: Option<String>,
+
+    #[serde(rename = "@styleCycle")]
+    style_cycle: Option<String>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct ColorMapping {
+    #[serde(rename = "@from")]
+    _from: String,
+    #[serde(rename = "@to")]
+    _to: String,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum ColorScale {
+    BipolarBlack,
+    BipolarWhite,
+    BipolarGray,
+    BipolarLightGray,
+    BipolarDarkGray,
+    Rainbow,
+    Circular,
+    Temperature,
+    Interpolated,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum ColorAffect {
+    Both,
+    #[serde(alias = "fill")]
+    #[default]
+    Main,
+    #[serde(alias = "stroke")]
+    Secondary,
+}
+
+impl ColorAffect {
+    pub fn includes_main(&self) -> bool {
+        matches!(self, ColorAffect::Both | ColorAffect::Main)
+    }
+
+    pub fn includes_secondary(&self) -> bool {
+        matches!(self, ColorAffect::Both | ColorAffect::Secondary)
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Location {
+    #[serde(rename = "@method")]
+    method: LocationMethod,
+    #[serde(rename = "@part", deserialize_with = "deserialize_part")]
+    part: (Axis2, Component),
+    #[serde(rename = "@target")]
+    target: Option<String>,
+    #[serde(rename = "@value")]
+    value: Option<LocationLength>,
+    #[serde(rename = "@min")]
+    min: Option<LocationLength>,
+    #[serde(rename = "@max")]
+    max: Option<LocationLength>,
+}
+
+fn deserialize_part<'de, D>(deserializer: D) -> Result<(Axis2, Component), D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let part = Part::deserialize(deserializer)?;
+    Ok(part.into_parts())
+}
+
+#[derive(Debug)]
+enum Location2 {
+    Fixed {
+        length: LocationLength,
+        min: Option<LocationLength>,
+        max: Option<LocationLength>,
+    },
+    Equals {
+        axis: Axis2,
+        component: Component,
+        index: usize,
+    },
+    SizeToContent {
+        axis: Axis2,
+        _min: Option<LocationLength>,
+        max: Option<LocationLength>,
+    },
+}
+
+impl Location2 {
+    fn is_size_to_content(&self) -> bool {
+        matches!(self, Self::SizeToContent { .. })
+    }
+
+    fn sort_key(&self) -> usize {
+        match self {
+            Location2::Fixed { .. } => 0,
+            Location2::Equals { .. } => 1,
+            Location2::SizeToContent { .. } => 2,
+        }
+    }
+
+    fn as_fixed(&self) -> Option<&LocationLength> {
+        match self {
+            Location2::Fixed { length, .. } => Some(length),
+            _ => None,
+        }
+    }
+
+    fn max(&self) -> Option<&LocationLength> {
+        match self {
+            Location2::Fixed { max, .. } | Location2::SizeToContent { max, .. } => max.as_ref(),
+            Location2::Equals { .. } => None,
+        }
+    }
+}
+
+impl Location {
+    fn axis(&self) -> Axis2 {
+        self.part.0
+    }
+
+    fn component(&self) -> Component {
+        self.part.1
+    }
+
+    fn decode(&self, ids: &HashMap<&String, usize>) -> Option<Location2> {
+        match self.method {
+            LocationMethod::Fixed => {
+                if let Some(length) = self.value {
+                    Some(Location2::Fixed {
+                        length,
+                        min: self.min,
+                        max: self.max,
+                    })
+                } else {
+                    None
+                }
+            }
+            LocationMethod::SizeToContent => {
+                if self.component() == Component::Length {
+                    Some(Location2::SizeToContent {
+                        axis: self.axis(),
+                        _min: self.min,
+                        max: self.max,
+                    })
+                } else {
+                    None
+                }
+            }
+            LocationMethod::Same => {
+                if let Some(target) = &self.target
+                    && let Some(index) = ids.get(target).copied()
+                {
+                    Some(Location2::Equals {
+                        axis: self.axis(),
+                        component: self.component(),
+                        index,
+                    })
+                } else {
+                    None
+                }
+            }
+            LocationMethod::Attach => {
+                if let Some(target) = &self.target
+                    && let Some(index) = ids.get(target).copied()
+                    && let Some(component) = self.component().opposite()
+                {
+                    Some(Location2::Equals {
+                        axis: self.axis(),
+                        component,
+                        index,
+                    })
+                } else {
+                    None
+                }
+            }
+        }
+    }
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum LocationMethod {
+    Fixed,
+    SizeToContent,
+    Same,
+    Attach,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq, Enum)]
+#[serde(rename_all = "camelCase")]
+enum Part {
+    Left,
+    Right,
+    Top,
+    Bottom,
+    Width,
+    Height,
+}
+
+impl Part {
+    fn into_parts(&self) -> (Axis2, Component) {
+        match self {
+            Part::Left => (Axis2::X, Component::Start),
+            Part::Right => (Axis2::X, Component::End),
+            Part::Top => (Axis2::Y, Component::Start),
+            Part::Bottom => (Axis2::Y, Component::End),
+            Part::Width => (Axis2::X, Component::Length),
+            Part::Height => (Axis2::Y, Component::Length),
+        }
+    }
+
+    #[allow(dead_code)]
+    fn opposite(&self) -> Option<Self> {
+        match self {
+            Part::Left => Some(Part::Right),
+            Part::Right => Some(Part::Left),
+            Part::Top => Some(Part::Bottom),
+            Part::Bottom => Some(Part::Top),
+            Part::Width | Part::Height => None,
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Faceting {
+    /// How to derive the domain for the facet cells.
+    #[serde(default, rename = "@method")]
+    method: FacetingMethod,
+
+    #[serde(default, rename = "@augmentEmptyCells")]
+    _augment_empty_cells: bool,
+
+    #[serde(default, rename = "$value")]
+    children: Vec<FacetingChild>,
+}
+
+/// How to derive the domain for facet axes.
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum FacetingMethod {
+    /// Produce a single domain for all of the facet cells by considering all of
+    /// their data together.
+    #[default]
+    Cross,
+
+    /// Each facet cell has its own independent domain based only on its own
+    /// data.
+    ///
+    /// Each facet cell will have its own axis (see [Axis]).
+    Nest,
+
+    /// Effect not known yet.
+    Dot,
+}
+
+impl From<FacetingMethod> for Operator {
+    fn from(value: FacetingMethod) -> Self {
+        match value {
+            FacetingMethod::Cross => Operator::Cross,
+            FacetingMethod::Nest => Operator::Nest,
+            FacetingMethod::Dot => Operator::Dot,
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+struct Facet<'a> {
+    series: &'a Series,
+    facet_level: Option<&'a FacetLevel>,
+}
+
+impl Faceting {
+    /// Returns the faceting for each axis, from innermost to outermost.
+    ///
+    /// This could decode `nest` and `blend` but those are super rare so we only
+    /// decode `cross` instead.
+    ///
+    /// This could decode layering into Z-axis faceting but the corpus doesn't
+    /// contain any examples of layering.
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+    ) -> EnumMap<Axis2, Vec<Facet<'a>>> {
+        let mut faceting = EnumMap::default();
+        let mut variable_index = 0;
+        for child in &self.children {
+            child.decode(series, facet_levels, &mut faceting, &mut variable_index);
+        }
+        faceting
+    }
+
+    fn expression(&self, warn: &mut dyn FnMut(GraphWarning)) -> Expression {
+        // Collect the children other than layers.  There should be zero or one.
+        let expression_children = self
+            .children
+            .iter()
+            .filter(|child| !child.is_layer())
+            .collect_vec();
+
+        // Warn if there's more than one.
+        if expression_children.len() > 1 {
+            warn(GraphWarning::MultipleFacetOperands);
+        }
+
+        // Convert the first one to an expression.
+        expression_children
+            .into_iter()
+            .next()
+            .map_or_default(|child| child.expression())
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FacetingChild {
+    VariableReference(VariableReference),
+    Dot(Cross),
+    Cross(Cross),
+    Nest(Nest),
+    Layer(Layer),
+}
+
+impl FacetingChild {
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        variable_index: &mut usize,
+    ) {
+        match self {
+            FacetingChild::Cross(cross) | FacetingChild::Dot(cross) => {
+                cross.decode(series, facet_levels, faceting, 0, variable_index)
+            }
+            FacetingChild::Nest(nest) => {
+                nest.decode(series, facet_levels, faceting, 0, variable_index)
+            }
+            FacetingChild::Layer(_) => todo!(),
+            FacetingChild::VariableReference(variable_reference) => variable_reference
+                .decode_faceting(series, facet_levels, faceting, 0, variable_index),
+        }
+    }
+
+    fn is_layer(&self) -> bool {
+        matches!(self, Self::Layer(_))
+    }
+
+    fn expression(&self) -> Expression {
+        match self {
+            FacetingChild::VariableReference(variable_reference) => variable_reference.expression(),
+            FacetingChild::Dot(cross) => cross.expression(Operator::Dot),
+            FacetingChild::Cross(cross) => cross.expression(Operator::Cross),
+            FacetingChild::Nest(nest) => nest.expression(),
+            FacetingChild::Layer(_) => Expression::Unity,
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Cross {
+    #[serde(rename = "$value")]
+    children: Vec<CrossChild>,
+}
+
+impl Cross {
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        start_index: usize,
+        variable_index: &mut usize,
+    ) {
+        for (child, index) in self.children.iter().zip(start_index..) {
+            child.decode(series, facet_levels, faceting, index, variable_index);
+        }
+    }
+    fn expression(&self, operator: Operator) -> Expression {
+        operator.apply(self.children.iter().map(|child| child.expression()))
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum CrossChild {
+    /// No dimensions along this axis.
+    Unity,
+    /// Dimensions along this axis.
+    Nest(Nest),
+    /// Single variable on this axis.
+    VariableReference(VariableReference),
+    /// Inner `cross` (very unusual).
+    Cross(Cross),
+}
+
+impl CrossChild {
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        index: usize,
+        variable_index: &mut usize,
+    ) {
+        match self {
+            CrossChild::Unity => (),
+            CrossChild::Nest(nest) => {
+                nest.decode(series, facet_levels, faceting, index, variable_index)
+            }
+            CrossChild::VariableReference(variable_reference) => {
+                variable_reference.decode_faceting(
+                    series,
+                    facet_levels,
+                    faceting,
+                    index,
+                    variable_index,
+                );
+            }
+            CrossChild::Cross(cross) => {
+                cross.decode(series, facet_levels, faceting, index, variable_index)
+            }
+        }
+    }
+
+    fn expression(&self) -> Expression {
+        match self {
+            CrossChild::Unity => Expression::Unity,
+            CrossChild::Nest(nest) => nest.expression(),
+            CrossChild::VariableReference(variable_reference) => variable_reference.expression(),
+            CrossChild::Cross(cross) => cross.expression(Operator::Cross),
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Nest {
+    #[serde(rename = "variableReference")]
+    variable_references: Vec<VariableReference>,
+}
+
+impl Nest {
+    fn decode<'a>(
+        &self,
+        series: &'a BTreeMap<&'a str, Series>,
+        facet_levels: &HashMap<usize, &'a FacetLevel>,
+        faceting: &mut EnumMap<Axis2, Vec<Facet<'a>>>,
+        index: usize,
+        variable_index: &mut usize,
+    ) {
+        for variable_reference in &self.variable_references {
+            variable_reference.decode_faceting(
+                series,
+                facet_levels,
+                faceting,
+                index,
+                variable_index,
+            );
+        }
+    }
+
+    fn expression(&self) -> Expression {
+        Operator::Nest.apply(
+            self.variable_references
+                .iter()
+                .map(VariableReference::expression),
+        )
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Layer {
+    #[serde(rename = "@variable")]
+    _variable: String,
+
+    #[serde(rename = "@value")]
+    _value: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FacetLayout {
+    #[serde(default, rename = "@topDown")]
+    _top_down: bool,
+
+    #[serde(default, rename = "$value")]
+    children: Vec<FacetLayoutChild>,
+}
+
+impl FacetLayout {
+    fn facet_levels(&self) -> impl Iterator<Item = &FacetLevel> {
+        self.children.iter().flat_map(|child| child.facet_level())
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum FacetLayoutChild {
+    SetCellProperties(#[allow(dead_code)] SetCellProperties),
+    FacetLevel(FacetLevel),
+    #[serde(other)]
+    Other,
+}
+
+impl FacetLayoutChild {
+    fn facet_level(&self) -> Option<&FacetLevel> {
+        match self {
+            FacetLayoutChild::FacetLevel(facet_level) => Some(facet_level),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetCellProperties {
+    #[serde(rename = "@applyToConverse", default)]
+    _apply_to_converse: bool,
+
+    #[serde(rename = "$value")]
+    _sets: Vec<Set>,
+
+    #[serde(rename = "union")]
+    _union: Option<Union>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Union {
+    #[serde(default, rename = "intersect")]
+    _intersects: Vec<Intersect>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Intersect {
+    #[serde(default, rename = "$value")]
+    _children: Vec<IntersectChild>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum IntersectChild {
+    Where(#[allow(dead_code)] Where),
+    #[serde(other)]
+    Other,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Where {
+    #[serde(rename = "@variable")]
+    _variable: String,
+    #[serde(rename = "@include")]
+    _include: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Set {
+    SetStyle(#[allow(dead_code)] SetStyle),
+    SetFrameStyle(#[allow(dead_code)] SetFrameStyle),
+    #[serde(other)]
+    Other,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetStyle {
+    #[serde(rename = "@target")]
+    _target: String,
+
+    /// The `id` for a [Style].
+    #[serde(rename = "@style")]
+    _style: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct SetFrameStyle {
+    #[serde(rename = "@target")]
+    _target: String,
+
+    /// The `id` for a [Style].
+    #[serde(rename = "@style")]
+    _style: String,
+}
+
+#[derive(Debug)]
+struct Coordinates {
+    dimensions: EnumMap<Axis3, Option<Dimension>>,
+}
+
+impl<'de> Deserialize<'de> for Coordinates {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        struct RawCoordinates {
+            #[serde(rename = "$value")]
+            #[serde(default)]
+            children: Vec<CoordinatesChild>,
+        }
+
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        enum CoordinatesChild {
+            Dimension(Dimension),
+            #[serde(other)]
+            Other,
+        }
+
+        let raw = RawCoordinates::deserialize(deserializer)?;
+        let mut dimensions = raw.children.into_iter().filter_map(|child| match child {
+            CoordinatesChild::Dimension(dimension) => Some(dimension),
+            CoordinatesChild::Other => None,
+        });
+        let dimensions =
+            EnumMap::from_array([dimensions.next(), dimensions.next(), dimensions.next()]);
+        Ok(Self { dimensions })
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Dimension {
+    #[serde(default, rename = "@unionDomain")]
+    _union_domain: bool,
+
+    /// A reference to an [IntervalDomain] or [CategoricalDomain].
+    #[serde(default, rename = "@domain")]
+    _domain: Option<String>,
+
+    #[serde(rename = "@upperMargin")]
+    upper_margin: Option<LocationLength>,
+
+    #[serde(rename = "@lowerMargin")]
+    lower_margin: Option<LocationLength>,
+
+    #[serde(default, rename = "@niceUpperMapping")]
+    _nice_upper_mapping: bool,
+
+    #[serde(default, rename = "@niceLowerMapping")]
+    _nice_lower_mapping: bool,
+
+    #[serde(default, rename = "@facetsShareScale")]
+    _facets_share_scale: bool,
+
+    #[serde(rename = "scale")]
+    _scale: Option<Scale>,
+
+    /// Maximum 2 axes.
+    ///
+    /// (Usually there's only one but you can have, for example, two Y axes, one
+    /// on the left, one on the right.)
+    #[serde(default)]
+    axis: Vec<Axis>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Scale {
+    #[serde(rename = "@method")]
+    _method: Method,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum Method {
+    Linear,
+    Log,
+    Power,
+    #[serde(rename = "@log.safe")]
+    LogSafe,
+    #[serde(rename = "@power.safe")]
+    PowerSafe,
+    Logit,
+    Probit,
+    Asn,
+    Atanh,
+    Prob,
+    #[serde(rename = "@cLoglog")]
+    CLoglog,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Enum)]
+#[serde(rename_all = "camelCase")]
+enum Clip {
+    None,
+    Cell,
+    Domain,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum Statistic {
+    BinStatistic(BinStatistic),
+    SummaryStatistic(SummaryStatistic),
+    DistributionStatistic(#[allow(dead_code)] DistributionStatistic),
+    TukeyStatistic,
+    #[serde(other)]
+    Other,
+}
+
+#[derive(Deserialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+#[serde(default)]
+struct BinStatistic {
+    /// Bin counts, one per dimension, space-separated.  Exclusive with
+    /// `bin_width`.
+    #[serde(rename = "@binCount")]
+    bin_count: Vec<f64>,
+
+    /// Bin widths, one per dimension, space-separated.  Exclusive with `bin_count`.
+    #[serde(rename = "@binWidth")]
+    bin_width: Vec<f64>,
+
+    /// Bin starting coordinates, space-separated, one per dimension.
+    #[serde(rename = "@binStart")]
+    bin_start: Vec<f64>,
+
+    #[serde(rename = "@gridType")]
+    shape: BinShape,
+
+    /// Whether to draw missing values.
+    #[serde(rename = "@augment")]
+    augment: bool,
+
+    /// Where to place the bins.
+    #[serde(rename = "@location")]
+    location: BinLocation,
+
+    /// How to bin.
+    #[serde(rename = "@method")]
+    method: BinMethod,
+
+    /// Dimensions to bin, semicolon-separated.  Dimension 1 is x.
+    #[serde(rename = "@dimensions")]
+    dimensions: String,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum BinShape {
+    #[default]
+    Square,
+    Hex,
+    Dot,
+    Voronoi,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum BinLocation {
+    /// Center of the bin, disregarding the data in the bin.
+    #[default]
+    #[serde(rename = "grid")]
+    Center,
+
+    /// Centroid of the data in the bin.
+    Centroid,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum BinMethod {
+    /// Bin across all the dimensions, without aggregating.  All the points in a
+    /// bin end up on top of each other.
+    #[default]
+    AllDimensions,
+
+    /// Like `AllDimensions` plus aggregation, so that each bin has only one
+    /// point.
+    Aggregate,
+
+    /// Bin across the dimensions except `Y`, without aggregation.
+    NotResponse,
+}
+
+#[derive(Deserialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+#[serde(default)]
+struct SummaryStatistic {
+    /// Converts interval summary statistics into a single value, the difference
+    /// between the upper and lower values.
+    #[serde(rename = "@convertIntervalToSingleValue")]
+    convert_interval_to_single_value: bool,
+
+    /// How to summarize the data.
+    #[serde(rename = "@method")]
+    method: SummaryMethod,
+
+    /// Base for percentage and fraction methods.
+    #[serde(rename = "@summaryBase")]
+    base: SummaryBase,
+
+    /// Percentage to use in a confidence interval summary.
+    #[serde(rename = "@percent")]
+    percent: Option<f64>,
+
+    /// Index of aesthetic or coordinate to use for 100%, when `base` is
+    /// [SummaryBase::Aesthetic] or [SummaryBase::Coordinate].
+    #[serde(rename = "@summaryIndex")]
+    index: Option<usize>,
+
+    /// If true, summary base is calculated across all panels.
+    /// If false, summary base is calculated within a panel.
+    #[serde(rename = "@summaryAcrossPanels")]
+    across_panels: bool,
+
+    /// How missing combinations are calculated.
+    #[serde(rename = "@augment")]
+    augment: Augment,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum SummaryMethod {
+    Mean,
+    Sum,
+    MidRange,
+    Percent,
+    PercentCount,
+    Fraction,
+    FractionCount,
+    Range,
+    ConfidenceIntervalOfMean,
+    ConfidenceIntervalIndividual,
+    #[default]
+    Count,
+    ConfidenceIntervalOfCount,
+    ConfidenceIntervalOfPercentCount,
+    ConfidenceIntervalOfFractionCount,
+    RowCount,
+    Stddev,
+    Stderr,
+    Median,
+    Max,
+    Min,
+    Variance,
+    CumulativeSum,
+    CumulativePercent,
+    CumulativePercentCount,
+    CumulativeFraction,
+    CumulativeFractionCount,
+    First,
+    Last,
+    Percentile,
+    Mode,
+    CountTrue,
+    PercentTrue,
+    FractionTrue,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum SummaryBase {
+    /// From data for a chart or panel.
+    #[default]
+    Global,
+
+    /// From data for aesthetics in a chart or panel.
+    Aesthetic,
+
+    /// From data for coordinates in a chart or panel.
+    Coordinate,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+enum Augment {
+    /// Treat missing values as zero.
+    #[default]
+    Zero,
+
+    /// Treat missing values as missing.
+    Missing,
+
+    /// Do not add any values for missing values.
+    None,
+}
+
+#[derive(Deserialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+#[serde(default)]
+struct DistributionStatistic {
+    #[serde(rename = "@distribution")]
+    distribution: Distribution,
+
+    #[serde(rename = "@parameter")]
+    parameter: Option<f64>,
+
+    #[serde(rename = "@secondaryParameter")]
+    secondary_parameter: Option<f64>,
+
+    #[serde(rename = "@segments")]
+    segments: Option<isize>,
+}
+
+#[derive(Copy, Clone, Deserialize, Debug, Default, PartialEq, Eq)]
+#[serde(rename_all = "lowercase")]
+enum Distribution {
+    Uniform,
+    #[default]
+    Normal,
+    T,
+    F,
+    ChiSquare,
+    Gamma,
+    Beta,
+    Exponential,
+    Logistic,
+    StudentizedRange,
+    Weibull,
+    Poisson,
+    BinomialNegative,
+    GaussianInverse,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Coordinate {
+    #[serde(rename = "@id")]
+    _id: Option<String>,
+
+    #[serde(rename = "@variable")]
+    variable: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct FacetLevel {
+    #[serde(rename = "@level")]
+    level: usize,
+
+    axis: Axis,
+}
+
+/// Controls the display of the axis for a dimension.
+///
+/// An axis consists of:
+///
+/// - Tick marks, if `major_ticks` and optionally `minor_ticks` are set.
+///
+/// - A label, if `label` is set.
+///
+/// # Faceted axes
+///
+/// In a faceted graph, the axis is shown either once or in every cell,
+/// according to the rules below, in order of decreasing priority:
+///
+/// - If the dimension has a polar transform, then the axis is drawn in every
+///   cell (because there's no reasonable way to draw it just once).
+///
+/// - With [AxisRepeat::Never], then the axis is drawn just once.  (This can
+///   produce deceptive results with [FacetingMethod::Nest], since in that case
+///   different facet cells might have different axes.)
+///
+/// - With [FacetingMethod::Nest] then the axis is drawn in every cell because
+///   each cell has a different axis.
+///
+/// - With [AxisRepeat::Always], the axis is drawn in every cell.
+///
+/// - Otherwise, the axis is drawn just once.
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Axis {
+    /// Controls where the axis labels and tick marks appear:
+    ///
+    /// - X-axis: false means below the graph, true means above the graph.
+    ///
+    /// - Y-axis: false means to the left of the graph, true to the right of the
+    ///   graph.
+    #[serde(rename = "@opposite", default)]
+    opposite: bool,
+
+    /// When faceting is present, this influences whether the axis appears a
+    /// single time or separately for each facet cell.
+    ///
+    /// This is ignored if the axis has a polar transformation.
+    #[serde(rename = "@repeat")]
+    _repeat: Option<AxisRepeat>,
+
+    /// When axes are repeated in each facet cell (as influenced by `repeat`),
+    /// this determines whether `label` appears once or in each facet cell:
+    ///
+    /// - If false, `label` appears once per facet cell.
+    ///
+    /// - If true, `label` appears just once.
+    ///
+    /// When axes appear just once, the axis label also appears only once
+    /// regardless of this setting.
+    #[serde(rename = "@combineAxisLabels", default)]
+    combine_axis_labels: bool,
+
+    /// If set, enables showing tick marks and labels along the axis.  If
+    /// `None`, the axis consists only of `label` (if any).
+    major_ticks: Option<MajorTicks>,
+
+    /// Label for the axis.
+    label: Option<Label>,
+}
+
+/// Whether an axis should appear a single time or separately for each facet
+/// cell.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Enum)]
+#[serde(rename_all = "camelCase")]
+enum AxisRepeat {
+    /// Repeat the axis in every facet cell.
+    Always,
+
+    /// Draw the axis only in one place.
+    ///
+    /// This can produce deceptive results if `faceting` specifies
+    /// `method="nest"`, since in that case different facet cells might have
+    /// different axes.
+    Never,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct MajorTicks {
+    /// The `id` for a [Style].
+    #[serde(rename = "@style")]
+    _style: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Label {
+    /// The `id` for a [Style].
+    #[serde(rename = "@style")]
+    style: Option<String>,
+
+    /// The `id` for a [Style].
+    #[serde(rename = "@textFrameStyle")]
+    _text_frame_style: Option<String>,
+
+    #[serde(rename = "@purpose")]
+    _purpose: Option<Purpose>,
+
+    #[serde(rename = "$value")]
+    child: LabelChild,
+}
+
+impl Label {
+    fn text(&self) -> Option<String> {
+        if let LabelChild::Text(text) = &self.child {
+            Some(text.iter().map(|text| &text.text).join(""))
+        } else {
+            None
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Enum)]
+#[serde(rename_all = "camelCase")]
+enum Purpose {
+    Title,
+    SubTitle,
+    SubSubTitle,
+    Layer,
+    Footnote,
+    SubFootnote,
+    Auto,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+enum LabelChild {
+    Text(Vec<Text>),
+    #[serde(other)]
+    Other,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Text {
+    /// If this is present, then `text` defines the content or the marker for a
+    /// footnote.
+    #[serde(rename = "@usesReference")]
+    _uses_reference: Option<NonZeroUsize>,
+
+    /// If this is present, then
+    #[serde(rename = "@definesReference")]
+    _defines_reference: Option<NonZeroUsize>,
+
+    #[serde(default, rename = "$text")]
+    text: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct LabelFrame {
+    #[serde(rename = "@id")]
+    id: Option<String>,
+    #[serde(default, rename = "location")]
+    locations: Vec<Location>,
+    label: Option<Label>,
+}
+
+impl LabelFrame {
+    fn text(&self) -> Option<String> {
+        if let Some(label) = &self.label {
+            label.text()
+        } else {
+            None
+        }
+    }
+}
+
+#[derive(Debug)]
+struct Container {
+    id: Option<String>,
+    locations: Vec<Location>,
+    children: Vec<AnyFrame>,
+}
+
+impl<'de> Deserialize<'de> for Container {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        struct RawContainer {
+            #[serde(rename = "@id")]
+            id: Option<String>,
+            #[serde(rename = "$value")]
+            #[serde(default)]
+            children: Vec<ContainerChild>,
+        }
+
+        #[derive(Deserialize, Debug)]
+        #[serde(rename_all = "camelCase")]
+        enum ContainerChild {
+            Location(Location),
+            Container(Container),
+            Graph(Graph),
+            Legend(Legend),
+            LabelFrame(LabelFrame),
+        }
+
+        let raw_container = RawContainer::deserialize(deserializer)?;
+        let mut locations = Vec::new();
+        let mut children = Vec::new();
+        for child in raw_container.children {
+            match child {
+                ContainerChild::Location(location) => locations.push(location),
+                ContainerChild::Container(container) => {
+                    children.push(AnyFrame::Container(container))
+                }
+                ContainerChild::Graph(graph) => children.push(AnyFrame::Graph(graph)),
+                ContainerChild::Legend(legend) => children.push(AnyFrame::Legend(legend)),
+                ContainerChild::LabelFrame(label_frame) => {
+                    children.push(AnyFrame::LabelFrame(label_frame))
+                }
+            }
+        }
+        Ok(Self {
+            id: raw_container.id,
+            locations,
+            children,
+        })
+    }
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+struct Legend {
+    #[serde(rename = "@id")]
+    id: Option<String>,
+
+    #[serde(rename = "location")]
+    locations: Vec<Location>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+struct LocationLengthRange {
+    base: LocationLength,
+    min: Option<LocationLength>,
+    max: Option<LocationLength>,
+}
+
+impl FromStr for LocationLengthRange {
+    type Err = ParseLengthError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut splitter = s.split(';');
+        let base = splitter
+            .next()
+            .ok_or(ParseLengthError::EmptyLength)?
+            .parse()?;
+        let min = splitter
+            .next()
+            .and_then(|s| s.trim().is_empty().not().then(|| s.parse()))
+            .transpose()?;
+        let max = splitter
+            .next()
+            .and_then(|s| s.trim().is_empty().not().then(|| s.parse()))
+            .transpose()?;
+        Ok(Self { base, min, max })
+    }
+}
+
+impl<'de> Deserialize<'de> for LocationLengthRange {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        String::deserialize(deserializer)?
+            .parse()
+            .map_err(D::Error::custom)
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum LocationLength {
+    Absolute(Length),
+    Percent(f64),
+}
+
+impl LocationLength {
+    fn as_pixels(&self, parent_length: Option<f64>) -> Option<f64> {
+        match self {
+            LocationLength::Absolute(length) => Some(length.as_px_f64()),
+            LocationLength::Percent(percentage) => parent_length.map(|p| p * (percentage / 100.0)),
+        }
+    }
+
+    #[allow(dead_code)]
+    fn as_percent(&self) -> Option<f64> {
+        match *self {
+            LocationLength::Absolute(_) => None,
+            LocationLength::Percent(percent) => Some(percent),
+        }
+    }
+}
+
+impl FromStr for LocationLength {
+    type Err = ParseLengthError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let s = s.split(';').next().ok_or(ParseLengthError::EmptyLength)?;
+        if let Some(s) = s.trim_end().strip_suffix("%")
+            && let Ok(percent) = s.trim().parse()
+        {
+            Ok(LocationLength::Percent(percent))
+        } else {
+            Ok(LocationLength::Absolute(s.parse()?))
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for LocationLength {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        String::deserialize(deserializer)?
+            .parse()
+            .map_err(D::Error::custom)
+    }
+}
+
+#[derive(Debug)]
+pub struct Frame<'a> {
+    frame_type: FrameType<'a>,
+    parent: Option<usize>,
+    children: Vec<usize>,
+    locations: EnumMap<Axis2, EnumMap<Component, Option<Location2>>>,
+    position: EnumMap<Axis2, Position>,
+}
+
+impl<'a> Frame<'a> {
+    fn new(frame_type: FrameType<'a>, parent: Option<usize>) -> Self {
+        Self {
+            frame_type,
+            parent,
+            children: Vec::new(),
+            locations: EnumMap::default(),
+            position: EnumMap::default(),
+        }
+    }
+    fn children(&self, frames: &'a [Frame<'a>]) -> impl Iterator<Item = &'a Frame<'a>> {
+        self.children.iter().copied().map(|index| &frames[index])
+    }
+    fn is_attached(&self, axis: Axis2, component: Component) -> bool {
+        self.locations[axis][component]
+            .as_ref()
+            .is_some_and(|location| match location {
+                Location2::Equals { component: c, .. } => component == c.opposite().unwrap(),
+                _ => false,
+            })
+    }
+    fn rect(&self) -> Option<Rect> {
+        Rect::try_from_fn(|axis| self.position[axis].span().ok_or(())).ok()
+    }
+    fn missing_values(&self) -> usize {
+        self.position
+            .values()
+            .map(|position| position.missing_values())
+            .sum()
+    }
+    #[allow(dead_code)]
+    fn demerits(&self, frames: &Frames) -> f64 {
+        let error_demerits = self.position.values().map(|p| p.demerits()).sum::<f64>();
+        let preferred = frames.preferred_size(self, false);
+        let diff = Coord(EnumMap::from_fn(|axis| {
+            self.position[axis]
+                .length()
+                .zip(preferred[axis])
+                .map_or(0.0, |(a, b)| a - b)
+        }));
+        let diff_demerits = diff.hypot();
+        let rect = self.rect().unwrap();
+        let child_demerits = self
+            .children(&frames.frames)
+            .map(|child| {
+                child.demerits(frames) + 20.0 * child.rect().unwrap().overflow_demerits(&rect)
+            })
+            .sum::<f64>();
+        self.frame_type.weight() * (error_demerits + diff_demerits) + child_demerits
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+struct Coord(EnumMap<Axis2, f64>);
+
+impl Coord {
+    fn hypot(&self) -> f64 {
+        self.0[Axis2::X].hypot(self.0[Axis2::Y])
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+struct Rect(EnumMap<Axis2, Span>);
+
+impl Rect {
+    fn new(x: Span, y: Span) -> Self {
+        Self(enum_map! { Axis2::X => x, Axis2::Y => y})
+    }
+
+    fn inset(&self, x: [f64; 2], y: [f64; 2]) -> Self {
+        Self::new(
+            Span(self.0[Axis2::X].0 + x[0], self.0[Axis2::X].1 - x[1]),
+            Span(self.0[Axis2::Y].0 + y[0], self.0[Axis2::Y].1 - y[1]),
+        )
+    }
+
+    fn subrect(&self, x: usize, nx: usize, y: usize, ny: usize) -> Self {
+        Self(enum_map! {
+            Axis2::X => self.0[Axis2::X].subdivide(nx, 5.0).nth(x),
+            Axis2::Y => self.0[Axis2::Y].subdivide(ny, 5.0).nth(y),
+        })
+    }
+
+    fn try_from_fn<E>(f: impl Fn(Axis2) -> Result<Span, E>) -> Result<Self, E> {
+        Ok(Self(EnumMap::try_from_fn(|axis| f(axis))?))
+    }
+
+    /// Returns a demerits score based on how much this rectangle overflows the
+    /// `limits` inside which it is supposed to remain.
+    fn overflow_demerits(&self, limits: &Rect) -> f64 {
+        self.0
+            .iter()
+            .map(|(axis, span)| span.overflow_demerits(&limits.0[axis]))
+            .sum()
+    }
+
+    #[allow(dead_code)]
+    fn as_svg(&self) -> Rectangle {
+        Rectangle::new()
+            .set("x", self.0[Axis2::X].0)
+            .set("y", self.0[Axis2::Y].0)
+            .set("width", self.0[Axis2::X].length())
+            .set("height", self.0[Axis2::Y].length())
+    }
+}
+
+/// A span divided into a number of equal-length segments separated by some
+/// amount of space.
+#[derive(Clone, Debug)]
+struct DividedSpan {
+    /// The divided span.
+    pub span: Span,
+
+    /// The space between segments.
+    pub spacing: f64,
+
+    /// The length of each segment.
+    pub each: f64,
+}
+
+impl DividedSpan {
+    /// Returns the start of the segment with the given 0-based `index`.
+    fn start(&self, index: usize) -> f64 {
+        self.span.0 + (self.each + self.spacing) * index as f64
+    }
+
+    /// Returns the end of the segment with the given 0-based `index`.
+    fn end(&self, index: usize) -> f64 {
+        self.start(index) + self.each
+    }
+
+    /// Returns the span of the segment with the given 0-based `index`.
+    fn nth(&self, index: usize) -> Span {
+        Span(self.start(index), self.end(index))
+    }
+
+    /// Returns the span for the given `range` of 0-based segments.  The span
+    /// will be empty if `range` is empty.
+    fn range(&self, range: &Range<usize>) -> Span {
+        let start = self.start(range.start);
+        let end = if !range.is_empty() {
+            self.end(range.end - 1)
+        } else {
+            start
+        };
+        Span(start, end)
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+struct Span(f64, f64);
+
+impl Span {
+    /// Returns this span divided into `count > 0` equally sized segments with
+    /// `spacing` between them.
+    fn subdivide(&self, count: usize, spacing: f64) -> DividedSpan {
+        if count == 0 {
+            DividedSpan {
+                span: *self,
+                spacing: 0.0,
+                each: 0.0,
+            }
+        } else {
+            let each = (self.length() - spacing * (count - 1) as f64) / count as f64;
+            if each > 0.0 {
+                DividedSpan {
+                    span: *self,
+                    spacing,
+                    each,
+                }
+            } else {
+                // No room for the spacing, so drop it.
+                DividedSpan {
+                    span: *self,
+                    spacing: 0.0,
+                    each: self.length() / count as f64,
+                }
+            }
+        }
+    }
+
+    /// Returns a demerits score based on how much this span overflows the
+    /// `limits` inside which it is supposed to remain.
+    fn overflow_demerits(&self, limits: &Span) -> f64 {
+        let min_demerits = if limits.0 > self.0 {
+            limits.0 - self.0
+        } else {
+            0.0
+        };
+        let max_demerits = if self.1 > limits.1 {
+            self.1 - limits.1
+        } else {
+            0.0
+        };
+        min_demerits + max_demerits
+    }
+
+    fn frac(&self, frac: f64) -> f64 {
+        frac * self.length() + self.0
+    }
+
+    fn length(&self) -> f64 {
+        self.1 - self.0
+    }
+
+    fn midpoint(&self) -> f64 {
+        self.frac(0.5)
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+struct Scaling {
+    /// Smallest tick.
+    pub base: f64,
+
+    /// Interval between ticks.
+    pub interval: f64,
+
+    /// Number of ticks.
+    pub n_ticks: usize,
+}
+
+impl Scaling {
+    /// An iterator that returns the positions of all the ticks.
+    pub fn ticks(&self) -> impl ExactSizeIterator<Item = f64> + DoubleEndedIterator {
+        (0..self.n_ticks).map(|i| self.base + self.interval * (i as f64))
+    }
+
+    #[allow(dead_code)]
+    pub fn first(&self) -> Option<f64> {
+        self.ticks().next()
+    }
+
+    #[allow(dead_code)]
+    pub fn last(&self) -> Option<f64> {
+        self.ticks().next_back()
+    }
+
+    pub fn new(base: f64, interval: f64, n_ticks: usize) -> Self {
+        Self {
+            base,
+            interval,
+            n_ticks,
+        }
+    }
+
+    pub fn maximum_fraction_digits(&self) -> usize {
+        // Base-10 logarithm of the interval:
+        // 10 => 1
+        // 1 => 0
+        // .1 => -1
+        // .2 => -1
+        // .5 => -1
+        // .01 => -2
+        // .02 => -2
+        // .05 => -2
+        let log10 = (self.interval * 1.01).log10().floor() as isize;
+
+        // 10 => 0
+        // 1 => 0
+        // .1 => 1
+        // .01 => 2
+        (-log10).max(0) as usize
+    }
+
+    pub fn for_range(range: impl Borrow<Range<f64>>) -> Scaling {
+        let range = range.borrow();
+        let r = range.end - range.start;
+        if r < 10.0 * f64::MIN_POSITIVE {
+            Scaling {
+                base: range.start,
+                interval: f64::MIN_POSITIVE,
+                n_ticks: 1,
+            }
+        } else {
+            // Round down the difference in the range to a power of 10, then divide
+            // by 10.  If `r` is a power of 10, then `basic_interval` will be `r /
+            // 10`; otherwise, it will be less than `r / 10` but more than `r /
+            // 100`.
+            //
+            // for a range of [5.5,9.2], basic_interval = 0.1;
+            // For a range of [0,10], basic_interval = 1;
+            // for a range of [55,92], basic_interval = 1;
+            // for a range of [0,100], basic_interval = 10;
+            // for a range of [555,922], basic_interval = 10;
+            // and so on.
+            let basic_interval = f64::powi(10.0, r.log10().floor() as i32 - 1);
+
+            // Find the most pleasing interval.
+            [1.0, 2.0, 5.0, 10.0]
+                .into_iter()
+                .map(|tick| {
+                    // Derive scaling for these parameters.
+                    let interval = tick * basic_interval;
+                    let base = (range.start / interval).floor() * interval;
+                    let n_ticks =
+                        (((range.end - base) / interval).ceil() as isize + 1).max(0) as usize;
+                    Scaling::new(base, interval, n_ticks)
+                })
+                .min_by_key(|scaling| OrderedFloat(scaling.score()))
+                .unwrap()
+        }
+    }
+
+    fn score(&self) -> f64 {
+        (5.0 - self.n_ticks as f64).abs()
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+struct Binning {
+    /// Starting point of the first bin.
+    pub start: f64,
+
+    /// Width of each bin.
+    pub width: f64,
+
+    /// Number of bins.
+    pub n: usize,
+}
+
+impl Binning {
+    pub fn bin(&self, value: f64) -> Option<f64> {
+        if value >= self.start
+            && let bin = ((value - self.start) / self.width).floor()
+            && let Some(bin) = <usize as NumCast>::from(bin)
+            && bin < self.n
+        {
+            Some(self.start + (bin as f64 + 0.5) * self.width)
+        } else {
+            None
+        }
+    }
+    pub fn new(start: f64, width: f64, n: usize) -> Self {
+        Self { start, width, n }
+    }
+    pub fn for_range(range: impl Borrow<Range<f64>>, n_bins: usize) -> Self {
+        let Range {
+            start: min,
+            end: max,
+        } = range.borrow().clone();
+        if min == max {
+            Self {
+                start: min - 0.5,
+                width: 1.0,
+                n: 1,
+            }
+        } else {
+            let scaling = Scaling::for_range(min..max);
+            let bin_width = (max - min) / n_bins as f64;
+            let (start, width) = if bin_width >= 2.0 * scaling.interval {
+                (
+                    scaling.base,
+                    (bin_width / scaling.interval).floor() * scaling.interval,
+                )
+            } else if bin_width >= 1.5 * scaling.interval {
+                let min = if min < scaling.base + scaling.interval * 0.5 {
+                    scaling.base
+                } else {
+                    scaling.base + 0.5 * scaling.interval
+                };
+                (min, scaling.interval * 1.5)
+            } else if bin_width >= scaling.interval {
+                (scaling.base, scaling.interval)
+            } else if bin_width >= (2.0 / 3.0) * scaling.interval {
+                let bin_width = (2.0 / 3.0) * scaling.interval;
+                let min = if min >= scaling.base + bin_width {
+                    scaling.base + bin_width
+                } else {
+                    scaling.base
+                };
+                (min, bin_width)
+            } else {
+                let mut i = 2.0;
+                while bin_width < scaling.interval / i {
+                    i += 1.0;
+                }
+                let bin_width = scaling.interval / i;
+                (
+                    ((min - scaling.base) / bin_width).floor() * bin_width + scaling.base,
+                    bin_width,
+                )
+            };
+            let start = start.min(min);
+            let mut n = NumCast::from(((max - start) / width).ceil().max(1.0)).unwrap_or(100usize);
+            let end = start + width * n as f64;
+            if end <= max {
+                n = n.saturating_add(1);
+            }
+            Binning::new(start, width, n)
+        }
+    }
+}
+
+/// Returns the number of bins suggested by Sturges, for `n` observations.
+fn sturges_rule(n: f64) -> Option<usize> {
+    NumCast::from((1.0 + n.log2()).ceil())
+}
+
+/// Returns the number of bins suggested by an approximation to Doane's rule,
+/// for `n` observations.
+#[allow(dead_code)]
+fn doane_rule(n: f64) -> Option<usize> {
+    NumCast::from((3.0 + n.log10() * n.log2()).ceil())
+}
+
+/// Returns the number of bins suggested by the Freedman-Diaconis rule for `n`
+/// observations with an interquartile range of `iqr`.
+#[allow(dead_code)]
+fn diaconis_rule(iqr: f64, n: f64) -> Option<usize> {
+    NumCast::from((2.0 * iqr / n.cbrt()).ceil())
+}
+
+#[derive(Debug)]
+struct FacetColumn<'a> {
+    columns: Range<usize>,
+    value: &'a SeriesValue,
+}
+
+impl<'a> FacetColumn<'a> {
+    fn label(&self, series: &Series) -> String {
+        self.value.label(&series.formatter)
+    }
+}
+
+#[derive(Debug)]
+struct FacetRow<'a> {
+    series: &'a Series,
+    facet_level: Option<&'a FacetLevel>,
+    columns: Vec<FacetColumn<'a>>,
+}
+
+impl<'a> FacetRow<'a> {
+    fn axis_label(&self) -> Option<String> {
+        self.facet_level?.axis.label.as_ref()?.text()
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+enum Side {
+    /// Top or left.
+    Top,
+    /// Bottom or right.
+    Bottom,
+}
+
+impl Side {
+    fn default_for_axis(axis: Axis2) -> Self {
+        match axis {
+            Axis2::X => Side::Bottom,
+            Axis2::Y => Side::Top,
+        }
+    }
+}
+
+impl Not for Side {
+    type Output = Side;
+
+    fn not(self) -> Self::Output {
+        match self {
+            Side::Top => Side::Bottom,
+            Side::Bottom => Side::Top,
+        }
+    }
+}
+
+/// A set of headings for the facets along the X or Y axis.
+///
+/// This structure applies to either axis, but the fields and code are written
+/// for X-axis facets.  It is rotated 90 degrees for Y axis display.
+struct FacetHeadings<'a> {
+    /// Width, fixed by the caller.
+    width: f64,
+
+    /// Axis labels (e.g. X-axis or Y-axis label).
+    ///
+    /// Ordinarily there is at most one axis on each side; if there happens to
+    /// be more than one then these go from innermost to outermost.  `axes`
+    /// appear inside `facets` if both are present on the same side.
+    axes: Vec<FacetAxisHeight<'a>>,
+
+    /// Hierarchical facet labels (e.g. the value for each facet, stacked if
+    /// there is more than one level of facets on an axis).
+    ///
+    /// Per-row heights on the default (left or top) side and the opposite side,
+    /// calculated based on `axis_facets` and `width`.
+    ///
+    /// From innermost to outermost.
+    facets: Vec<FacetRowHeight<'a>>,
+
+    /// Axis tick marks.
+    ticks: Option<(Side, Vec<(f64, String)>, f64)>,
+
+    /// Number of facets.
+    n_facets: usize,
+}
+
+struct FacetAxisHeight<'a> {
+    axis: &'a Axis,
+    text: String,
+    side: Side,
+    height: f64,
+}
+
+struct FacetRowHeight<'a> {
+    row: FacetRow<'a>,
+    side: Side,
+    axis_label_height: f64,
+    facet_label_height: f64,
+}
+
+impl<'a> FacetRowHeight<'a> {
+    fn height(&self) -> f64 {
+        self.axis_label_height + self.facet_label_height
+    }
+}
+
+impl<'a> FacetHeadings<'a> {
+    fn total_height(&self) -> f64 {
+        self.height(Side::Top) + self.height(Side::Bottom)
+    }
+
+    fn height(&self, side: Side) -> f64 {
+        self.axes
+            .iter()
+            .filter(|axis| axis.side == side)
+            .map(|axis| axis.height)
+            .sum::<f64>()
+            + self
+                .facets
+                .iter()
+                .filter(|row_height| row_height.side == side)
+                .map(|row_height| row_height.height())
+                .sum::<f64>()
+            + if let Some((ticks_side, _, height)) = &self.ticks
+                && *ticks_side == side
+            {
+                *height
+            } else {
+                0.0
+            }
+    }
+
+    fn new(
+        default_side: Side,
+        facets: &'a [Facet<'a>],
+        axis_domain: &'a AxisDomain<'a>,
+        width: f64,
+        dimension: Option<&'a Dimension>,
+        font_metrics: &FontMetrics,
+        rotate_ticks: bool,
+    ) -> Self {
+        let axis_facets = AxisFacets::new(facets);
+        let n_facets = axis_facets.len();
+        let span = Span(0.0, width).subdivide(n_facets, 5.0);
+        let facets = axis_facets
+            .rows
+            .into_iter()
+            .map(|row| {
+                let side = if let Some(facet_level) = row.facet_level
+                    && facet_level.axis.opposite
+                {
+                    !default_side
+                } else {
+                    default_side
+                };
+                let axis_label_height = row.axis_label().map_or(0.0, |label| {
+                    font_metrics.measure_height_for_width(&label, width)
+                });
+
+                let mut facet_label_height = 0.0f64;
+                for column in &row.columns {
+                    let label = column.label(&row.series);
+                    if !label.is_empty() {
+                        let column_width = span.range(&column.columns).length();
+                        let tmp = font_metrics.measure_height_for_width(&label, column_width);
+                        facet_label_height = facet_label_height.max(tmp);
+                    }
+                }
+
+                FacetRowHeight {
+                    row,
+                    side,
+                    axis_label_height,
+                    facet_label_height,
+                }
+            })
+            .collect();
+        let axes = dimension
+            .iter()
+            .flat_map(|d| &d.axis)
+            .filter_map(|axis| {
+                if let Some(label) = &axis.label
+                    && let Some(text) = label.text()
+                {
+                    let width = if axis.combine_axis_labels {
+                        width
+                    } else {
+                        span.each
+                    };
+                    let height = font_metrics.measure_height_for_width(&text, width);
+                    Some(FacetAxisHeight {
+                        axis,
+                        text,
+                        side: if axis.opposite {
+                            !default_side
+                        } else {
+                            default_side
+                        },
+                        height,
+                    })
+                } else {
+                    None
+                }
+            })
+            .collect();
+        let ticks = axis_domain.axis.map(|axis| {
+            let ticks = axis_domain.ticks();
+            let ticks_height = ticks
+                .iter()
+                .map(|(_, s)| {
+                    font_metrics.measure(s)[if rotate_ticks { Axis2::X } else { Axis2::Y }]
+                })
+                .max_by(|a, b| a.total_cmp(b))
+                .unwrap_or_default();
+            (
+                if axis.opposite {
+                    !default_side
+                } else {
+                    default_side
+                },
+                ticks,
+                ticks_height + 10.0,
+            )
+        });
+        Self {
+            width,
+            axes,
+            ticks,
+            facets,
+            n_facets,
+        }
+    }
+
+    fn render(&self, side: Side, rotation: f64) -> Group {
+        let span = Span(0.0, self.width).subdivide(self.n_facets, 5.0);
+        let mut group = Group::new();
+
+        fn render_ticks(
+            this: &FacetHeadings,
+            ticks: &[(f64, String)],
+            group: &mut Group,
+            span: &DividedSpan,
+            y: f64,
+            _height: f64,
+        ) {
+            for facet_span in (0..this.n_facets).map(|index| span.nth(index)) {
+                for (frac, _label) in ticks {
+                    let x = facet_span.frac(*frac);
+                    group.append(
+                        Line::new()
+                            .set("x1", x)
+                            .set("x2", x)
+                            .set("y1", y)
+                            .set("y2", y + 10.0)
+                            .set("stroke", "black"),
+                    );
+                }
+            }
+        }
+
+        fn render_tick_labels(
+            this: &FacetHeadings,
+            ticks: &[(f64, String)],
+            group: &mut Group,
+            span: &DividedSpan,
+            y: f64,
+            _height: f64,
+            rotation: f64,
+        ) {
+            for facet_span in (0..this.n_facets).map(|index| span.nth(index)) {
+                for (frac, label) in ticks {
+                    let x = facet_span.frac(*frac);
+                    let label = if label.is_empty() { "x" } else { &label };
+                    /*group.append(
+                        Circle::new()
+                            .set("cx", x)
+                            .set("cy", y)
+                            .set("r", "5px")
+                            .set("fill", "none")
+                            .set("stroke", "red"),
+                    );*/
+                    if rotation == 0.0 {
+                        group.append(
+                            svg::node::element::Text::new(label)
+                                .set("x", x)
+                                .set("y", y)
+                                .set("dominant-baseline", "hanging")
+                                .set("text-anchor", "middle"),
+                        );
+                    } else {
+                        group.append(
+                            svg::node::element::Text::new(label)
+                                .set("x", x)
+                                .set("y", y)
+                                .set("dominant-baseline", "middle")
+                                .set("text-anchor", "end")
+                                .set("transform", format!("rotate(90 {x} {y})")),
+                        );
+                    }
+                }
+            }
+        }
+
+        let mut y = 0.0;
+        if let Some((ticks_side, ticks, height)) = &self.ticks
+            && side == *ticks_side
+            && side == Side::Bottom
+        {
+            render_ticks(self, ticks, &mut group, &span, y, *height);
+            render_tick_labels(self, ticks, &mut group, &span, y + 10.0, *height, rotation);
+            y += height;
+        }
+        /*
+        group.append(
+            Rect(enum_map! {
+                Axis2::X => Span(0.0, self.width),
+                Axis2::Y => Span(0.0, self.height(side))
+            })
+            .as_svg()
+            .set("fill", "none")
+            .set("stroke", "red"),
+        );*/
+        for axis in self.axes.iter().filter(|axis| axis.side == side) {
+            if axis.axis.combine_axis_labels {
+                group.append(
+                    svg::node::element::Text::new(&axis.text)
+                        .set("x", self.width / 2.0)
+                        .set("y", y)
+                        .set("dominant-baseline", "hanging")
+                        .set("text-anchor", "middle"),
+                );
+            } else {
+                for index in 0..self.n_facets {
+                    group.append(
+                        svg::node::element::Text::new(&axis.text)
+                            .set("x", span.nth(index).midpoint())
+                            .set("y", y)
+                            .set("dominant-baseline", "hanging")
+                            .set("text-anchor", "middle"),
+                    );
+                }
+            };
+            y += axis.height;
+        }
+        let heights = self
+            .facets
+            .iter()
+            .rev()
+            .filter(|height| height.side == side);
+        let parents = std::iter::once(None).chain(heights.clone().map(Some));
+        for (height, parent) in zip(heights, parents) {
+            if let Some(axis_label) = height.row.axis_label() {
+                let parent = if let Some(facet_level) = height.row.facet_level
+                    && !facet_level.axis.combine_axis_labels
+                {
+                    parent
+                } else {
+                    None
+                };
+
+                match parent {
+                    Some(parent) => {
+                        // XXX the corresponding code for measurement in
+                        // FacetHeading::new doesn't use the right width for
+                        // this case.
+                        for column in &parent.row.columns {
+                            let span = span.range(&column.columns);
+                            group.append(
+                                svg::node::element::Text::new(&axis_label)
+                                    .set("x", span.midpoint())
+                                    .set("y", y)
+                                    .set("dominant-baseline", "hanging")
+                                    .set("text-anchor", "middle"),
+                            );
+                        }
+                    }
+                    None => {
+                        group.append(
+                            svg::node::element::Text::new(axis_label)
+                                .set("x", self.width / 2.0)
+                                .set("y", y)
+                                .set("dominant-baseline", "hanging")
+                                .set("text-anchor", "middle"),
+                        );
+                    }
+                }
+            }
+            y += height.axis_label_height;
+
+            for column in &height.row.columns {
+                let label = column.label(&height.row.series);
+                if !label.is_empty() {
+                    let span = span.range(&column.columns);
+                    group.append(
+                        svg::node::element::Text::new(label)
+                            .set("x", span.midpoint())
+                            .set("y", y)
+                            .set("dominant-baseline", "hanging")
+                            .set("text-anchor", "middle"),
+                    );
+                }
+            }
+            y += height.facet_label_height;
+        }
+        if let Some((ticks_side, ticks, height)) = &self.ticks
+            && *ticks_side == side
+            && side == Side::Top
+        {
+            render_tick_labels(
+                self,
+                ticks,
+                &mut group,
+                &span,
+                if rotation != 0.0 {
+                    y + (*height - 10.0)
+                } else {
+                    y
+                },
+                *height,
+                rotation,
+            );
+            render_ticks(
+                self,
+                ticks,
+                &mut group,
+                &span,
+                y + (*height - 10.0),
+                *height,
+            );
+            y += height;
+        }
+        group
+    }
+}
+
+#[derive(Debug)]
+struct AxisFacets<'a> {
+    /// From innermost to outermost.
+    ///
+    /// If there are no rows, then there is a single facet.
+    /// If rows exist but they have zero columns, then there are no facets.
+    rows: Vec<FacetRow<'a>>,
+}
+
+impl<'a> AxisFacets<'a> {
+    /// `facets` goes from innermost to outermost.
+    fn new(facets: &'a [Facet<'a>]) -> Self {
+        let mut rows = facets
+            .iter()
+            .map(|facet| FacetRow {
+                series: facet.series,
+                facet_level: facet.facet_level,
+                columns: Vec::new(),
+            })
+            .collect_vec();
+        for (column_index, (increment, values)) in
+            LexIter::new(facets.iter().map(|facet| facet.series.unique_values.len())).enumerate()
+        {
+            for (row_index, (facet, row)) in zip(facets, &mut rows).enumerate() {
+                if row_index <= increment {
+                    row.columns.push(FacetColumn {
+                        columns: column_index..column_index + 1,
+                        value: &facet.series.unique_values[values[row_index]],
+                    });
+                } else {
+                    row.columns.last_mut().unwrap().columns.end += 1;
+                }
+            }
+        }
+        Self { rows }
+    }
+
+    /// The number of facets.
+    ///
+    /// If there are no rows, then there is a single facet.
+    /// If rows exist but they have zero columns, then there are no facets.
+    fn len(&self) -> usize {
+        self.rows.first().map_or(1, |row| row.columns.len())
+    }
+}
+
+struct LexIter<C> {
+    counts: C,
+    next: SmallVec<[usize; 2]>,
+    zeroed: usize,
+    remaining: usize,
+}
+
+impl<C> LexIter<C>
+where
+    C: ExactSizeIterator<Item = usize> + Clone,
+{
+    fn new<I>(counts: I) -> Self
+    where
+        I: IntoIterator<IntoIter = C>,
+    {
+        let counts = counts.into_iter();
+        Self {
+            next: smallvec![0; counts.len()],
+            zeroed: counts.len(),
+            remaining: counts.clone().product(),
+            counts,
+        }
+    }
+}
+
+impl<C> Iterator for LexIter<C>
+where
+    C: ExactSizeIterator<Item = usize> + Clone,
+{
+    type Item = (usize, SmallVec<[usize; 2]>);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.remaining == 0 {
+            None
+        } else {
+            let result = (self.zeroed, self.next.clone());
+            self.zeroed = self.counts.len();
+            for (index, (next, count)) in zip(&mut self.next, self.counts.clone()).enumerate() {
+                *next += 1;
+                if *next < count {
+                    self.zeroed = index;
+                    break;
+                }
+                *next = 0;
+            }
+            self.remaining -= 1;
+            Some(result)
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.remaining, Some(self.remaining))
+    }
+}
+
+impl<C> ExactSizeIterator for LexIter<C> where C: ExactSizeIterator<Item = usize> + Clone {}
+
+#[derive(Clone, Debug)]
+struct FacetIterator<'a> {
+    facets: &'a [Facet<'a>],
+    next: SmallVec<[usize; 2]>,
+    remaining: usize,
+}
+
+impl<'a> FacetIterator<'a> {
+    fn new(facets: &'a [Facet<'a>]) -> Self {
+        Self {
+            facets,
+            next: smallvec![0; facets.len()],
+            remaining: if facets.is_empty() {
+                1
+            } else {
+                facets
+                    .into_iter()
+                    .map(|facet| facet.series.unique_values.len())
+                    .product()
+            },
+        }
+    }
+
+    fn into_values(
+        self,
+    ) -> impl Iterator<Item = SmallVec<[&'a SeriesValue; 2]>> + ExactSizeIterator + Clone {
+        let facets = self.facets;
+        self.map(move |indexes| {
+            indexes
+                .into_iter()
+                .zip(facets)
+                .map(|(index, facet)| &facet.series.unique_values[index])
+                .collect()
+        })
+    }
+}
+
+impl<'a> Iterator for FacetIterator<'a> {
+    type Item = SmallVec<[usize; 2]>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.remaining == 0 {
+            None
+        } else {
+            let result = self.next.iter().copied().collect();
+            for (index, facet) in self.next.iter_mut().zip(self.facets) {
+                *index += 1;
+                if *index < facet.series.unique_values.len() {
+                    break;
+                }
+                *index = 0;
+            }
+            self.remaining -= 1;
+            Some(result)
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.remaining, Some(self.remaining))
+    }
+}
+
+impl<'a> ExactSizeIterator for FacetIterator<'a> {}
+
+struct SemicolonSplit<'a>(Option<&'a str>);
+
+impl<'a> SemicolonSplit<'a> {
+    fn new(s: &'a str) -> Self {
+        Self(Some(s))
+    }
+}
+
+impl<'a> Iterator for SemicolonSplit<'a> {
+    type Item = Cow<'a, str>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let start = self.0?;
+        let position = match start.find(&[';', '\\']) {
+            None => {
+                self.0 = None;
+                return Some(Cow::from(start));
+            }
+            Some(position) => position,
+        };
+        if start.as_bytes()[position] == b';' {
+            self.0 = Some(&start[position + 1..]);
+            return Some(Cow::from(&start[..position]));
+        }
+
+        let mut output = String::new();
+        let mut iter = start.chars();
+        while let Some(c) = iter.next() {
+            if c == ';' {
+                self.0 = Some(iter.as_str());
+                return Some(Cow::from(output));
+            } else if c != '\\' {
+                output.push(c)
+            } else if let Some(c) = iter.next() {
+                output.push(c);
+            }
+        }
+        self.0 = None;
+        Some(Cow::from(output))
+    }
+}
+
+trait ElementExt {
+    fn with_id(self, id: &Option<impl AsRef<str>>) -> Self;
+}
+
+impl<T> ElementExt for T
+where
+    T: Node,
+{
+    fn with_id(mut self, id: &Option<impl AsRef<str>>) -> Self {
+        if let Some(id) = id {
+            self.assign("id", String::from(id.as_ref()));
+        }
+        self
+    }
+}
+
+impl crate::output::Item {
+    pub fn read_graph(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
+        let path = path.as_ref();
+        let xml = std::fs::read(path)?;
+        let (data, data_file_name) = if let Some(file_name) = path.file_name()
+            && let Some(file_name) = file_name.to_str()
+            && let Some(stem) = file_name.strip_suffix("_chart.xml")
+        {
+            let data_file_name = path.with_file_name(format!("{stem}_chartData.bin"));
+            match std::fs::read(&data_file_name) {
+                Ok(data) => (Some(data), Some(data_file_name)),
+                Err(error) if error.kind() == ErrorKind::NotFound => (None, None),
+                Err(error) => return Err(error.into()),
+            }
+        } else {
+            (None, None)
+        };
+        Ok(crate::output::Graph {
+            xml,
+            data,
+            csv: None,
+        }
+        .into_item()
+        .with_spv_info(
+            SpvInfo::new().with_members(Some(SpvMembers::Graph(SpvGraphMembers {
+                xml: path.to_string_lossy().into_owned(),
+                data: data_file_name.map(|path| path.to_string_lossy().into_owned()),
+                csv: None,
+            }))),
+        ))
+    }
+}
+
+impl crate::output::Graph {
+    pub fn binary_data<F>(&self, mut warn: F) -> Result<DataSources, GraphError>
+    where
+        F: FnMut(GraphWarning),
+    {
+        if let Some(data) = &self.data {
+            Ok(
+                LegacyBin::from_data(&data, &mut |w| warn(GraphWarning::LegacyBinWarning(w)))
+                    .map_err(GraphError::BinaryDataError)?,
+            )
+        } else {
+            Ok(Default::default())
+        }
+    }
+
+    pub fn csv_data(&self) -> Result<Option<DataSource>, GraphError> {
+        match &self.csv {
+            Some(csv) => Ok(Some(DataSource::from_csv_data(&csv).unwrap() /*XXX*/)),
+            _ => Ok(None),
+        }
+    }
+
+    pub fn to_svg<F>(
+        &self,
+        _members: Option<&SpvGraphMembers>,
+        mut warn: F,
+    ) -> Result<Document, GraphError>
+    where
+        F: FnMut(GraphWarning),
+    {
+        let visualization: Visualization = serde_path_to_error::deserialize(
+            &mut quick_xml::de::Deserializer::from_reader(Cursor::new(&self.xml)),
+        )?;
+        visualization.decode(self.binary_data(&mut warn)?, self.csv_data()?, &mut warn)
+    }
+
+    /// Returns a version of this graph that is most suitable for including in
+    /// an SPV file.  This reverses the transformation made by
+    /// [to_chart_xml](Self::to_chart_xml): while the output produced by that
+    /// function is valid and SPSS will display the graphs, an apparent bug in
+    /// some SPSS versions keeps it from editing them.
+    ///
+    /// Most graphs don't need changes; for those, this function returns the
+    /// original graph.
+    pub fn to_spv(&self) -> Cow<'_, crate::output::Graph> {
+        if self.data.is_some() {
+            return Cow::Borrowed(self);
+        }
+
+        // Copy the input to the output, except for parsing data out of all the
+        // `embeddedSource` elements and replacing them with `userSource` stubs.
+        let mut data = IndexMap::new();
+        let mut reader = Reader::from_reader(self.xml.as_slice());
+        let mut writer = Writer::new(Vec::new());
+        loop {
+            let start_position = reader.buffer_position() as usize;
+            let event = match reader.read_event() {
+                Ok(event) => event,
+                Err(_) => todo!(),
+            };
+
+            fn id_attribute<'a>(start: &'a BytesStart) -> Option<Cow<'a, str>> {
+                for attribute in start.attributes() {
+                    if let Ok(attribute) = attribute
+                        && attribute.key.local_name().as_ref() == b"id"
+                        && let Ok(value) = attribute.unescape_value()
+                    {
+                        return Some(value);
+                    }
+                }
+                None
+            }
+
+            match &event {
+                Event::Start(start) => {
+                    let local_name = start.local_name();
+                    if local_name.as_ref() == b"embeddedSource"
+                        && let Some(id) = id_attribute(start)
+                    {
+                        reader.read_to_end(start.to_end().name()).expect("xxx");
+                        let end_position = reader.buffer_position() as usize;
+                        let data_source =
+                            EmbeddedSource::from_xml(&self.xml[start_position..end_position])
+                                .expect("xxx")
+                                .decode();
+                        writer
+                            .create_element("userSource")
+                            .with_attribute(("id", id.as_ref()))
+                            .write_empty()
+                            .expect("writing to a vector shouldn't fail");
+                        data.insert(id.into_owned(), data_source);
+                    } else {
+                        writer
+                            .write_event(event)
+                            .expect("writing to a vector shouldn't fail");
+                    }
+                }
+                Event::Eof => break,
+                _ => writer
+                    .write_event(event)
+                    .expect("writing to a vector shouldn't fail"),
+            }
+        }
+
+        if !data.is_empty() {
+            // Turn the parsed data into a `.bin` member.
+            Cow::Owned(crate::output::Graph {
+                xml: writer.into_inner(),
+                data: Some(DataSources(data).to_legacy_bin()),
+                csv: self.csv.clone(),
+            })
+        } else {
+            // We didn't find any `embeddedSource` elements, so stick with what
+            // we had.
+            Cow::Borrowed(self)
+        }
+    }
+
+    /// Returns a version of this graph that can be written as a separate XML
+    /// file outside any SPV file, by embedding the data in the XML.
+    pub fn to_chart_xml<F>(&self, mut warn: F) -> Result<Vec<u8>, GraphError>
+    where
+        F: FnMut(GraphWarning),
+    {
+        let mut reader = Reader::from_reader(self.xml.as_slice());
+        let mut writer = Writer::new(Vec::new());
+        loop {
+            let event = match reader.read_event() {
+                Ok(event) => event,
+                Err(_) => todo!(),
+            };
+
+            fn id_attribute<'a>(start: &'a BytesStart) -> Option<Cow<'a, str>> {
+                for attribute in start.attributes() {
+                    if let Ok(attribute) = attribute
+                        && attribute.key.local_name().as_ref() == b"id"
+                        && let Ok(value) = attribute.unescape_value()
+                    {
+                        return Some(value);
+                    }
+                }
+                None
+            }
+
+            match &event {
+                Event::Empty(start) => {
+                    let local_name = start.local_name();
+                    if local_name.as_ref() == b"userSource"
+                        && let Some(id) = id_attribute(start)
+                        && let Some(data) = self.binary_data(&mut warn)?.0.get(&*id)
+                    {
+                        data.write_as_embedded_source(&mut writer, &id)
+                            .expect("writing to a vector shouldn't fail");
+                    } else if local_name.as_ref() == b"delimitedFileSource"
+                        && let Some(id) = id_attribute(start)
+                        && let Some(csv) = self.csv_data()?
+                    {
+                        csv.write_as_embedded_source(&mut writer, &id)
+                            .expect("writing to a vector shouldn't fail");
+                    } else {
+                        writer
+                            .write_event(event)
+                            .expect("writing to a vector shouldn't fail");
+                    }
+                }
+                Event::Eof => break,
+                _ => writer
+                    .write_event(event)
+                    .expect("writing to a vector shouldn't fail"),
+            }
+        }
+        Ok(writer.into_inner())
+    }
+}
+
+static PALETTE: [Color; 30] = [
+    Color::new_u32(0x1192e8),
+    Color::new_u32(0x005d5d),
+    Color::new_u32(0x9f1853),
+    Color::new_u32(0xfa4d56),
+    Color::new_u32(0x570408),
+    Color::new_u32(0x198038),
+    Color::new_u32(0x002d9c),
+    Color::new_u32(0xee538b),
+    Color::new_u32(0xb28600),
+    Color::new_u32(0x009d9a),
+    Color::new_u32(0x012749),
+    Color::new_u32(0x8a3800),
+    Color::new_u32(0xa56eff),
+    Color::new_u32(0xece6d0),
+    Color::new_u32(0x454647),
+    Color::new_u32(0x5cca88),
+    Color::new_u32(0xd05334),
+    Color::new_u32(0xcc7fe4),
+    Color::new_u32(0xe1bc1d),
+    Color::new_u32(0xed4b4b),
+    Color::new_u32(0x1ccdcd),
+    Color::new_u32(0x5c7148),
+    Color::new_u32(0xe18b0e),
+    Color::new_u32(0x092672),
+    Color::new_u32(0x5a645e),
+    Color::new_u32(0x9b0000),
+    Color::new_u32(0xcface3),
+    Color::new_u32(0x969191),
+    Color::new_u32(0x3feb7c),
+    Color::new_u32(0x6929c4),
+];
+
+#[cfg(test)]
+mod tests {
+    use std::{fmt::Write as _, path::Path, str::FromStr};
+
+    use approx::assert_relative_eq;
+    use itertools::Itertools;
+    use smallvec::smallvec;
+
+    use crate::{
+        output::pivot::{Length, ParseLengthError, tests::assert_lines_eq},
+        spv::read::graph::{
+            Binning, LexIter, LocationLength, LocationLengthRange, Scaling, SemicolonSplit,
+            sturges_rule,
+        },
+    };
+
+    #[test]
+    fn lex_iter() {
+        // Normal lexicographical iteration.
+        assert_eq!(
+            LexIter::new([2, 2, 2]).collect_vec(),
+            vec![
+                (3, smallvec![0, 0, 0]),
+                (0, smallvec![1, 0, 0]),
+                (1, smallvec![0, 1, 0]),
+                (0, smallvec![1, 1, 0]),
+                (2, smallvec![0, 0, 1]),
+                (0, smallvec![1, 0, 1]),
+                (1, smallvec![0, 1, 1]),
+                (0, smallvec![1, 1, 1]),
+            ]
+        );
+
+        // Normal lexicographical iteration.
+        assert_eq!(
+            LexIter::new([1, 2, 3]).collect_vec(),
+            vec![
+                (3, smallvec![0, 0, 0]),
+                (1, smallvec![0, 1, 0]),
+                (2, smallvec![0, 0, 1]),
+                (1, smallvec![0, 1, 1]),
+                (2, smallvec![0, 0, 2]),
+                (1, smallvec![0, 1, 2]),
+            ]
+        );
+
+        // An empty set of counts yields a single empty element.
+        assert_eq!(LexIter::new([]).collect_vec(), vec![(0, smallvec![])]);
+
+        // But a count with value 0 yields no elements.
+        assert_eq!(LexIter::new([2, 0, 2]).collect_vec(), vec![]);
+    }
+
+    #[test]
+    fn length() {
+        // Individual lengths.
+        assert_eq!(
+            LocationLength::from_str("9pt"),
+            Ok(LocationLength::Absolute(Length(0.125)))
+        );
+        assert_eq!(
+            LocationLength::from_str("50%"),
+            Ok(LocationLength::Percent(50.0))
+        );
+        assert_eq!(
+            LocationLength::from_str(" "),
+            Err(ParseLengthError::EmptyLength),
+        );
+
+        // Ranges.
+        assert_eq!(
+            LocationLengthRange::from_str("9pt"),
+            Ok(LocationLengthRange {
+                base: LocationLength::Absolute(Length(0.125)),
+                min: None,
+                max: None
+            })
+        );
+        assert_eq!(
+            LocationLengthRange::from_str("9pt;;"),
+            Ok(LocationLengthRange {
+                base: LocationLength::Absolute(Length(0.125)),
+                min: None,
+                max: None
+            })
+        );
+        assert_eq!(
+            LocationLengthRange::from_str("9pt;;18pt"),
+            Ok(LocationLengthRange {
+                base: LocationLength::Absolute(Length(0.125)),
+                min: None,
+                max: Some(LocationLength::Absolute(Length(0.25))),
+            })
+        );
+        assert_eq!(
+            LocationLengthRange::from_str("9pt;0pt"),
+            Ok(LocationLengthRange {
+                base: LocationLength::Absolute(Length(0.125)),
+                min: Some(LocationLength::Absolute(Length(0.0))),
+                max: None
+            })
+        );
+        assert_eq!(
+            LocationLengthRange::from_str("9pt;0pt;18pt"),
+            Ok(LocationLengthRange {
+                base: LocationLength::Absolute(Length(0.125)),
+                min: Some(LocationLength::Absolute(Length(0.0))),
+                max: Some(LocationLength::Absolute(Length(0.25))),
+            })
+        );
+        assert_eq!(
+            LocationLengthRange::from_str(";9pt;"),
+            Err(ParseLengthError::EmptyLength)
+        );
+    }
+
+    #[test]
+    fn scaling() {
+        for (range, (base, interval, n_ticks), maximum_fraction_digits) in [
+            (5.5..9.2, (5.0, 1.0, 6), 0),
+            (0.55..0.92, (0.5, 0.1, 6), 1),
+            (0.0..10.0, (0.0, 2.0, 6), 0),
+            (55.0..92.0, (50.0, 10.0, 6), 0),
+            (0.0..100.0, (0.0, 20.0, 6), 0),
+        ] {
+            let expected = Scaling::new(base, interval, n_ticks);
+            let scaling = Scaling::for_range(&range);
+            assert_eq!(scaling, expected);
+            assert_eq!(scaling.ticks().count(), scaling.n_ticks);
+            assert_eq!(scaling.ticks().next(), Some(scaling.base));
+            assert_eq!(scaling.first(), Some(scaling.base));
+            assert!(scaling.base <= range.start);
+            assert!(scaling.last().unwrap() >= range.end);
+            assert_eq!(scaling.maximum_fraction_digits(), maximum_fraction_digits);
+        }
+    }
+
+    #[test]
+    fn binning() {
+        for (range, (start, width, n)) in [
+            (5.5..9.2, (5.5, 0.5, 8)),
+            (0.0..10.0, (0.0, 4.0 / 3.0, 8)),
+            (55.0..92.0, (55.0, 5.0, 8)),
+            (0.0..100.0, (0.0, 40.0 / 3.0, 8)),
+        ] {
+            let expected = Binning::new(start, width, n);
+            let binning = Binning::for_range(&range, sturges_rule(50.0).unwrap());
+            assert_eq!(binning.start, expected.start);
+            assert_relative_eq!(binning.width, expected.width);
+            assert_eq!(binning.n, expected.n);
+        }
+    }
+
+    #[test]
+    fn semicolon_split() {
+        fn split(s: &str) -> Vec<String> {
+            SemicolonSplit::new(s).map(|s| s.into_owned()).collect_vec()
+        }
+
+        assert_eq!(split("a;b;c"), vec!["a", "b", "c"]);
+        assert_eq!(split("a;b;c;"), vec!["a", "b", "c", ""]);
+        assert_eq!(split(""), vec![""]);
+        assert_eq!(split("a\\;b\\;c"), vec!["a;b;c"]);
+        assert_eq!(split("a\\;b\\;c"), vec!["a;b;c"]);
+        assert_eq!(split("a\\;b\\"), vec!["a;b"]);
+        assert_eq!(split("a\\;b;c\\;d"), vec!["a;b", "c;d"]);
+    }
+
+    fn test_graph_file(name: &str) {
+        let input_filename = Path::new("src/spv/testdata")
+            .join(name)
+            .with_extension("xml");
+        let graph = crate::output::Graph {
+            xml: std::fs::read(&input_filename).unwrap(),
+            data: None,
+            csv: None,
+        };
+        let expected_filename = input_filename.with_extension("expected.svg");
+        let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+        let mut actual = String::new();
+        let svg = graph
+            .to_svg(None, |w| writeln!(&mut actual, "{w}").unwrap())
+            .unwrap();
+        writeln!(&mut actual, "{svg}").unwrap();
+        let actual_filename = input_filename.with_extension("actual.svg");
+        if expected != actual {
+            if std::env::var("PSPP_REFRESH_EXPECTED").is_ok() {
+                std::fs::write(&expected_filename, actual).unwrap();
+                panic!("{}: refreshed output", expected_filename.display());
+            } else {
+                let _ = std::fs::write(&actual_filename, actual.as_bytes());
+                eprintln!("note: rerun with PSPP_REFRESH_EXPECTED=1 to refresh expected output");
+            }
+        } else {
+            let _ = std::fs::remove_file(&actual_filename);
+        }
+        assert_lines_eq(
+            &expected,
+            expected_filename.display(),
+            &actual,
+            actual_filename.display(),
+        );
+    }
+
+    #[test]
+    fn graph1() {
+        test_graph_file("graph1");
+    }
+
+    #[test]
+    fn graph2() {
+        test_graph_file("graph2");
+    }
+
+    #[test]
+    fn graph3() {
+        test_graph_file("graph3");
+    }
+
+    #[test]
+    fn graph4() {
+        test_graph_file("graph4");
+    }
+
+    #[test]
+    fn graph5() {
+        test_graph_file("graph5");
+    }
+}
diff --git a/rust/pspp/src/spv/read/graph/format.rs b/rust/pspp/src/spv/read/graph/format.rs
new file mode 100644 (file)
index 0000000..9de7813
--- /dev/null
@@ -0,0 +1,1193 @@
+use enum_map::EnumMap;
+use serde::Deserialize;
+use smallstr::SmallString;
+use smallvec::{SmallVec, smallvec};
+use std::{
+    borrow::Cow,
+    fmt::{Display, Write as _},
+    iter::{once, repeat_n},
+    ops::Range,
+};
+
+use crate::{
+    calendar::calendar_offset_to_date_time,
+    data::Datum,
+    format::group_digits,
+    locale::{DayLength, HourCount, Item, Locale, MonthLength, PatternType, data::EN},
+    spv::read::graph::{DayType, Format, HourFormat, Mdy, MonthFormat, RawScientific},
+    util::ToSmallString as _,
+};
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum Negatives {
+    /// Show negative values with a minus sign.
+    #[default]
+    Minus,
+    /// Show negative values as their absolute values.
+    Absolute,
+    /// Show negative values inside parentheses.
+    Parentheses,
+}
+
+impl Negatives {
+    pub fn prefix(&self) -> &'static str {
+        match self {
+            Negatives::Minus => "-",
+            Negatives::Absolute => "",
+            Negatives::Parentheses => "(",
+        }
+    }
+    pub fn suffix(&self) -> &'static str {
+        match self {
+            Negatives::Minus => "",
+            Negatives::Absolute => "",
+            Negatives::Parentheses => ")",
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum NegativeLocation {
+    /// Show negatives inside the prefix or suffix, e.g. `$-123` or `(55)%`.
+    #[default]
+    Inside,
+    /// Show negatives outside the prefix or suffix, e.g. `-$123` or (55%)`.
+    Outside,
+}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Deserialize)]
+pub enum Scientific {
+    #[default]
+    Auto,
+    OnlyForSmall(f64),
+    WhenNeeded,
+    True,
+    False,
+}
+
+impl Scientific {
+    fn choose_expression(&self, number: f64) -> NumberExpression {
+        let scientific = number != 0.0
+            && match *self {
+                Scientific::Auto => !(0.001..1.0e8).contains(&number.abs()),
+                Scientific::OnlyForSmall(small) => number.abs() < small,
+                Scientific::True => true,
+                Scientific::WhenNeeded | Scientific::False => false,
+            };
+        if scientific {
+            NumberExpression::Scientific
+        } else {
+            NumberExpression::Decimal
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize)]
+enum NumberExpression {
+    // 123.45
+    #[default]
+    Decimal,
+
+    // 1.2345e2
+    Scientific,
+}
+
+#[derive(Clone, Debug)]
+pub struct NumberFormatter {
+    pub _locale: &'static Locale<'static>,
+
+    pub minimum_fraction_digits: Option<usize>,
+    pub maximum_fraction_digits: Option<usize>,
+    pub minimum_integer_digits: Option<usize>,
+    pub maximum_integer_digits: Option<usize>,
+    pub negatives: Negatives,
+    pub negative_location: NegativeLocation,
+
+    /// A number to divide the value by before formatting.
+    ///
+    /// # Examples
+    ///
+    /// - `scaling_factor` of 1000 with a `suffix` of `thousands`.
+    ///
+    /// - `scaling_factor` of 1e-6 with a `suffix` of `μ`.
+    pub scaling_factor: f64,
+
+    /// When to use scientific notation.
+    pub scientific: Scientific,
+
+    /// Decimal point (commonly `.` or `,`).
+    pub decimal: char,
+    /// Grouping character (commonly `,` or `.`), only if grouping is enabled.
+    pub group: Option<char>,
+    /// For indicating errors (commonly `*`).
+    pub error: char,
+
+    /// Prefix (such as `$`).
+    pub prefix: String,
+    /// Suffix (such as `%`).
+    pub suffix: String,
+}
+
+#[derive(Clone, Debug)]
+pub enum Formatter {
+    Number(NumberFormatter),
+    Date(DateFormatter),
+}
+
+impl Formatter {
+    pub fn default_for_locale(locale: &'static Locale<'static>) -> Self {
+        Self::Number(NumberFormatter::default_for_locale(locale))
+    }
+
+    pub fn new(format: &Format, locale: &'static Locale<'static>) -> Self {
+        if format.base_format.is_some() {
+            Self::Date(DateFormatter::new(format, locale))
+        } else {
+            Self::Number(NumberFormatter::new(format, locale))
+        }
+    }
+
+    pub fn display<'a, T>(&'a self, value: &'a Datum<T>) -> impl Display
+    where
+        T: Display,
+    {
+        DisplayFormatter {
+            formatter: self,
+            value,
+        }
+    }
+}
+
+struct DisplayFormatter<'a, T> {
+    formatter: &'a Formatter,
+    value: &'a Datum<T>,
+}
+
+impl<'a, T> Display for DisplayFormatter<'a, T>
+where
+    T: Display,
+{
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self.value {
+            Datum::Number(Some(number)) => match self.formatter {
+                Formatter::Number(data_number_format) => data_number_format.format(f, *number),
+                Formatter::Date(data_date_format) => data_date_format.format(f, *number),
+            },
+            Datum::Number(None) => f.write_char('.'),
+            Datum::String(s) => s.fmt(f),
+        }
+    }
+}
+
+impl Default for NumberFormatter {
+    fn default() -> Self {
+        Self::default_for_locale(&EN)
+    }
+}
+
+impl NumberFormatter {
+    pub fn default_for_locale(_locale: &'static Locale<'static>) -> Self {
+        Self {
+            minimum_fraction_digits: None,
+            maximum_fraction_digits: None,
+            minimum_integer_digits: None,
+            maximum_integer_digits: None,
+            negatives: Negatives::default(),
+            negative_location: NegativeLocation::default(),
+            scaling_factor: 1.0,
+            scientific: Scientific::Auto,
+            decimal: _locale.decimal(),
+            group: Some(_locale.group()),
+            error: '*',
+            prefix: String::new(),
+            suffix: String::new(),
+            _locale,
+        }
+    }
+
+    pub fn new(format: &Format, _locale: &'static Locale<'static>) -> Self {
+        Self {
+            maximum_fraction_digits: format.maximum_fraction_digits,
+            minimum_fraction_digits: format.minimum_fraction_digits,
+            minimum_integer_digits: format.minimum_integer_digits,
+            maximum_integer_digits: format.maximum_integer_digits,
+            negatives: if format.hide_negatives {
+                Negatives::Absolute
+            } else if format.negatives_in_parentheses {
+                Negatives::Parentheses
+            } else {
+                Negatives::Minus
+            },
+            negative_location: if format.negatives_outside {
+                NegativeLocation::Outside
+            } else {
+                NegativeLocation::Inside
+            },
+            scaling_factor: format.scaling_factor.unwrap_or(1.0),
+            scientific: match format.scientific {
+                Some(RawScientific::OnlyForSmall) => {
+                    if let Some(small) = format.small
+                        && small > 0.0
+                    {
+                        Scientific::OnlyForSmall(small)
+                    } else {
+                        Scientific::False
+                    }
+                }
+                Some(RawScientific::False) => Scientific::False,
+                Some(RawScientific::True) => Scientific::False,
+                Some(RawScientific::WhenNeeded) => Scientific::WhenNeeded,
+                None => Scientific::Auto,
+            },
+            decimal: format.decimal(_locale),
+            group: format
+                .use_grouping
+                .unwrap_or(true)
+                .then(|| format.group(_locale)),
+            error: format.error_character.unwrap_or('*'),
+            prefix: format.prefix.clone(),
+            suffix: format.suffix.clone(),
+            _locale,
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn with_scientific(self, scientific: Scientific) -> Self {
+        Self { scientific, ..self }
+    }
+    #[allow(dead_code)]
+    pub fn with_minimum_fraction_digits(self, minimum_fraction_digits: Option<usize>) -> Self {
+        Self {
+            minimum_fraction_digits,
+            ..self
+        }
+    }
+    pub fn with_maximum_fraction_digits(self, maximum_fraction_digits: Option<usize>) -> Self {
+        Self {
+            maximum_fraction_digits,
+            ..self
+        }
+    }
+    #[allow(dead_code)]
+    pub fn with_minimum_integer_digits(self, minimum_integer_digits: Option<usize>) -> Self {
+        Self {
+            minimum_integer_digits,
+            ..self
+        }
+    }
+    #[allow(dead_code)]
+    pub fn with_maximum_integer_digits(self, maximum_integer_digits: Option<usize>) -> Self {
+        Self {
+            maximum_integer_digits,
+            ..self
+        }
+    }
+    #[allow(dead_code)]
+    pub fn with_negatives(self, negatives: Negatives) -> Self {
+        Self { negatives, ..self }
+    }
+    #[allow(dead_code)]
+    pub fn with_negative_location(self, negative_location: NegativeLocation) -> Self {
+        Self {
+            negative_location,
+            ..self
+        }
+    }
+    #[allow(dead_code)]
+    pub fn with_prefix(self, prefix: impl Into<String>) -> Self {
+        Self {
+            prefix: prefix.into(),
+            ..self
+        }
+    }
+    #[allow(dead_code)]
+    pub fn with_suffix(self, suffix: impl Into<String>) -> Self {
+        Self {
+            suffix: suffix.into(),
+            ..self
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn display<'a>(&'a self, value: &'a Datum<String>) -> impl Display {
+        DisplayNumberFormatter {
+            format: self,
+            value,
+        }
+    }
+
+    fn format(&self, f: &mut std::fmt::Formatter, number: f64) -> std::fmt::Result {
+        let number = number / self.scaling_factor;
+        match self.scientific.choose_expression(number) {
+            NumberExpression::Scientific => self.format_scientific(f, number),
+            NumberExpression::Decimal => self.format_decimal(f, number),
+        }
+    }
+
+    fn format_scientific(&self, f: &mut std::fmt::Formatter, number: f64) -> std::fmt::Result {
+        //let body = format!("{:.*E}", self.maximum_fraction_digits, number.abs()).to_small_string();
+        let body = format!("{:E}", number.abs()).to_small_string();
+        let pn = ParsedNumber::from_scientific(body).unwrap();
+        let pn = if let Some(maximum_fraction_digits) = self.maximum_fraction_digits
+            && pn.fraction.len() > maximum_fraction_digits
+        {
+            let body = format!("{:.*E}", maximum_fraction_digits, number.abs()).to_small_string();
+            ParsedNumber::from_scientific(body).unwrap()
+        } else {
+            pn
+        };
+        let pn = pn.with_fraction_digits(self.minimum_fraction_digits);
+        let is_negative = number.is_sign_negative() && pn.is_nonzero();
+        self.format_prefixes(f, is_negative)?;
+        pn.format(f, self.decimal, self.group)?;
+        self.format_suffixes(f, is_negative)
+    }
+
+    fn format_decimal(&self, f: &mut std::fmt::Formatter, number: f64) -> std::fmt::Result {
+        let body = number.abs().to_small_string();
+        let pn = ParsedNumber::from_decimal(body).unwrap();
+        let body;
+        let pn = if let Some(maximum_fraction_digits) = self.maximum_fraction_digits
+            && pn.fraction.len() > maximum_fraction_digits
+        {
+            body = format!("{:.*}", maximum_fraction_digits, number.abs()).to_small_string();
+            ParsedNumber::from_decimal(body).unwrap()
+        } else {
+            pn
+        };
+        if let Some(maximum_integer_digits) = self.maximum_integer_digits
+            && pn.integer > maximum_integer_digits
+        {
+            return f.write_char(self.error);
+        }
+        let pn = pn
+            .with_integer_digits(self.minimum_integer_digits)
+            .with_fraction_digits(self.minimum_fraction_digits);
+        let is_negative = number.is_sign_negative() && pn.is_nonzero();
+        self.format_prefixes(f, is_negative)?;
+        pn.format(f, self.decimal, self.group)?;
+        self.format_suffixes(f, is_negative)
+    }
+
+    fn format_prefixes(&self, f: &mut std::fmt::Formatter, is_negative: bool) -> std::fmt::Result {
+        match is_negative.then_some(self.negative_location) {
+            Some(NegativeLocation::Inside) => {
+                f.write_str(&self.prefix)?;
+                f.write_str(self.negatives.prefix())
+            }
+            Some(NegativeLocation::Outside) => {
+                f.write_str(self.negatives.prefix())?;
+                f.write_str(&self.prefix)
+            }
+            None => f.write_str(&self.prefix),
+        }
+    }
+
+    fn format_suffixes(&self, f: &mut std::fmt::Formatter, is_negative: bool) -> std::fmt::Result {
+        match is_negative.then_some(self.negative_location) {
+            Some(NegativeLocation::Inside) => {
+                f.write_str(self.negatives.suffix())?;
+                f.write_str(&self.suffix)
+            }
+            Some(NegativeLocation::Outside) => {
+                f.write_str(&self.suffix)?;
+                f.write_str(self.negatives.suffix())
+            }
+            None => f.write_str(&self.suffix),
+        }
+    }
+
+    #[allow(dead_code)]
+    fn format_affixes(&self, body: &str, is_negative: bool) -> String {
+        let mut prefixes: SmallVec<[&str; 2]> = smallvec![self.prefix.as_str()];
+        let mut suffixes: SmallVec<[&str; 2]> = smallvec![self.suffix.as_str()];
+        if is_negative {
+            let neg_prefix = self.negatives.prefix();
+            let neg_suffix = self.negatives.suffix();
+            match self.negative_location {
+                NegativeLocation::Inside => {
+                    prefixes.push(neg_prefix);
+                    suffixes.insert(0, neg_suffix);
+                }
+                NegativeLocation::Outside => {
+                    prefixes.insert(0, neg_prefix);
+                    suffixes.push(neg_suffix);
+                }
+            }
+        }
+        prefixes
+            .iter()
+            .copied()
+            .chain(once(&*body))
+            .chain(suffixes.iter().copied())
+            .collect()
+    }
+}
+
+struct DisplayNumberFormatter<'a> {
+    format: &'a NumberFormatter,
+    value: &'a Datum<String>,
+}
+
+impl<'a> Display for DisplayNumberFormatter<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let number = match self.value {
+            Datum::Number(Some(number)) => *number,
+            Datum::Number(None) => return f.write_char('.'),
+            Datum::String(s) => return s.fmt(f),
+        };
+
+        self.format.format(f, number)
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct DateFormatter {
+    locale: &'static Locale<'static>,
+    scaling_factor: f64,
+    items: Vec<Item<'static>>,
+}
+
+impl DateFormatter {
+    pub fn new(format: &Format, locale: &'static Locale<'static>) -> Self {
+        let mut show_date = format.show_era.unwrap_or(false)
+            || format.show_year.unwrap_or(false)
+            || format.show_quarter.unwrap_or(false)
+            || format.show_week.unwrap_or(false)
+            || format.show_day_of_week.unwrap_or(false)
+            || format.show_day.unwrap_or(false);
+        let mut show_time = format.show_hour.unwrap_or(false)
+            || format.show_minute.unwrap_or(false)
+            || format.show_second.unwrap_or(false);
+        if !show_date && !show_time {
+            show_date = true;
+            show_time = true;
+        }
+
+        let date_style = show_date.then(|| {
+            if format.show_day_of_week.unwrap_or(false) {
+                PatternType::Full
+            } else if format.show_month.unwrap_or(true)
+                && (format.month_format == Some(MonthFormat::Long)
+                    || format.month_format == Some(MonthFormat::Short)
+                        && (format.show_year == Some(false) || format.show_day == Some(false)))
+            {
+                PatternType::Medium
+            } else {
+                PatternType::Short
+            }
+        });
+        let time_style = show_time.then(|| {
+            if format.show_second.unwrap_or(true) {
+                PatternType::Long
+            } else {
+                PatternType::Full
+            }
+        });
+        let template = match (date_style, time_style) {
+            (Some(date_style), Some(time_style)) => locale.date_time_items(time_style, date_style),
+            (Some(date_style), None) => locale.date_items(date_style),
+            (None, Some(time_style)) => locale.time_items(time_style),
+            (None, None) => unreachable!(),
+        }
+        .into_iter();
+
+        let mut output = Vec::new();
+        for item in template {
+            match item {
+                Item::CalendarYear2
+                | Item::CalendarYear(_)
+                | Item::WeekYear2
+                | Item::WeekYear(_) => {
+                    match (format.show_year.unwrap_or(true), format.year_abbreviation) {
+                        (true, Some(true)) => output.push(Item::CalendarYear2),
+                        (true, Some(false)) => output.push(Item::CalendarYear(4)),
+                        (true, None) => output.push(item),
+                        (false, _) => (),
+                    }
+                }
+                Item::Era | Item::Quarter | Item::FractionalSecond { .. } | Item::Zone(_) => (),
+                Item::MonthNumber { .. } | Item::Month(_) | Item::Week { .. } => {
+                    if format.show_quarter.unwrap_or(false) {
+                        if !format.quarter_prefix.is_empty() {
+                            output.push(Item::Literal(Cow::from(format.quarter_prefix.as_str())));
+                        }
+                        output.push(Item::Quarter);
+                        if !format.quarter_suffix.is_empty() {
+                            output.push(Item::Literal(Cow::from(format.quarter_suffix.as_str())));
+                        }
+                    } else if format.show_month.unwrap_or(true) {
+                        match format.month_format {
+                            Some(MonthFormat::Long) => output.push(Item::Month(MonthLength::Wide)),
+                            Some(MonthFormat::Short) => {
+                                output.push(Item::Month(MonthLength::Abbreviated))
+                            }
+                            Some(MonthFormat::Number) => {
+                                output.push(Item::MonthNumber { pad: false })
+                            }
+                            Some(MonthFormat::PaddedNumber) => {
+                                output.push(Item::MonthNumber { pad: true })
+                            }
+                            None => output.push(item),
+                        }
+                    } else if format.show_week.unwrap_or(true) {
+                        output.push(Item::Week {
+                            pad: format.week_padding.unwrap_or(item.pad().unwrap_or(false)),
+                        });
+                        if !format.week_suffix.is_empty() {
+                            output.push(Item::Literal(Cow::from(format.week_suffix.as_str())));
+                        }
+                    }
+                }
+                Item::DayOfMonth { pad } => {
+                    if format.show_day.unwrap_or(true) {
+                        let pad = format.day_padding.unwrap_or(pad);
+                        match format.day_type.unwrap_or(DayType::Month) {
+                            DayType::Month => output.push(Item::DayOfMonth { pad }),
+                            DayType::Year => output.push(Item::DayOfYear(if pad { 3 } else { 1 })),
+                        }
+                    }
+                }
+                Item::DayOfYear(n) => {
+                    if format.show_day.unwrap_or(true) {
+                        let n = match format.day_padding {
+                            Some(false) => 1,
+                            Some(true) => 3,
+                            None => n,
+                        };
+                        match format.day_type.unwrap_or(DayType::Month) {
+                            DayType::Month => output.push(Item::DayOfMonth { pad: n > 1 }),
+                            DayType::Year => output.push(Item::DayOfYear(n)),
+                        }
+                    }
+                }
+                Item::DayOfWeek(_) => {
+                    if format.show_day_of_week.unwrap_or(true) {
+                        match format.day_of_week_abbreviation {
+                            Some(true) => output.push(Item::DayOfWeek(DayLength::Abbreviated)),
+                            Some(false) => output.push(Item::DayOfWeek(DayLength::Wide)),
+                            None => output.push(item),
+                        }
+                    }
+                }
+                Item::DayPeriod(_) | Item::FlexibleDayPeriod(_) => {
+                    if format.show_hour.unwrap_or(true)
+                        && !matches!(
+                            format.hour_format,
+                            Some(HourFormat::As12 | HourFormat::As24)
+                        )
+                    {
+                        output.push(item);
+                    }
+                }
+                Item::Hour {
+                    mut count,
+                    mut base,
+                    mut pad,
+                } => {
+                    if format.show_hour.unwrap_or(true) {
+                        if format.hour_format == Some(HourFormat::As24)
+                            && count != HourCount::TwentyFour
+                        {
+                            count = HourCount::TwentyFour;
+                            base = !base;
+                        }
+                        pad = format.hour_padding.unwrap_or(pad);
+                        output.push(Item::Hour { count, base, pad });
+                    }
+                }
+                Item::Minute { .. } => {
+                    if format.show_minute.unwrap_or(true) {
+                        if let Some(pad) = format.minute_padding {
+                            output.push(Item::Minute { pad });
+                        } else {
+                            output.push(item);
+                        }
+                    }
+                }
+                Item::Second { .. } => {
+                    if format.show_second.unwrap_or(true) {
+                        if let Some(pad) = format.second_padding {
+                            output.push(Item::Second { pad })
+                        } else {
+                            output.push(item);
+                        }
+                        if format.show_millis.unwrap_or(false) {
+                            let max_digits = format.maximum_fraction_digits.unwrap_or(3);
+                            let min_digits = format
+                                .minimum_fraction_digits
+                                .unwrap_or(max_digits)
+                                .min(max_digits);
+                            if max_digits > 0 {
+                                output.extend([
+                                    Item::Literal(format.decimal(locale).to_string().into()),
+                                    Item::FractionalSecond {
+                                        min_digits,
+                                        max_digits,
+                                    },
+                                ]);
+                            }
+                        }
+                    }
+                }
+                Item::Error { .. } => output.push(item),
+                Item::Literal(_) => {
+                    if !format.hidden_separator {
+                        output.push(item);
+                    }
+                }
+            }
+        }
+        if format.show_era.unwrap_or(false) {
+            output.extend([Item::Literal(Cow::from(" ")), Item::Era]);
+        }
+        // Now shuffle according to mdyOrder.
+        if let Some(mdy_order) = format.mdy_order {
+            let mut mdy_items: EnumMap<Mdy, Option<Item<'_>>> = EnumMap::default();
+            let mut indexes = SmallVec::<[usize; 3]>::new();
+            for (index, item) in output.iter().enumerate() {
+                if let Ok(key) = Mdy::try_from(item)
+                    && mdy_items[key].is_none()
+                {
+                    mdy_items[key] = Some(item.clone());
+                    indexes.push(index);
+                }
+            }
+            for (index, mdy) in std::iter::zip(indexes, mdy_order) {
+                output[index] = mdy_items[mdy].as_ref().unwrap().clone();
+            }
+        }
+
+        Self {
+            locale,
+            items: output.into_iter().map(|item| item.to_owned()).collect(),
+            scaling_factor: format.scaling_factor.unwrap_or(1.0),
+        }
+    }
+
+    fn format(&self, f: &mut std::fmt::Formatter, number: f64) -> std::fmt::Result {
+        let number = number / self.scaling_factor;
+        let Some(date_time) = calendar_offset_to_date_time(number) else {
+            return f.write_char('*');
+        };
+        for item in &self.items {
+            write!(f, "{}", item.display(self.locale, &date_time, 0))?;
+        }
+        Ok(())
+    }
+}
+
+#[derive(Debug)]
+struct ParsedNumber {
+    s: SmallString<[u8; 32]>,
+    i_pad: usize,
+    integer: usize,
+    fraction: Range<usize>,
+    f_pad: usize,
+    exponent: Option<usize>,
+}
+
+impl ParsedNumber {
+    fn new(s: SmallString<[u8; 32]>) -> Self {
+        let (head, exponent) = if let Some(exponent) = s.rfind('E') {
+            (&s[..exponent], Some(exponent))
+        } else {
+            (&*s, None)
+        };
+
+        let (integer, fraction) = if let Some(decimal) = head.find('.') {
+            (decimal, decimal + 1..head.len())
+        } else {
+            (head.len(), 0..0)
+        };
+
+        // The integer and fractional parts must be all ASCII digits.
+        // The exponent must be "E" followed by a sign and ASCII digits.
+        /*
+        debug_assert!(integer.chars().all(|c| c.is_ascii_digit()));
+        debug_assert!(fraction.chars().all(|c| c.is_ascii_digit()));
+        debug_assert!(exponent.is_empty() || exponent.starts_with('E'));
+        debug_assert!(
+            exponent
+                .chars()
+                .next()
+                .iter()
+                .copied()
+                .all(|c| c == '-' || c == '+' || c.is_ascii_digit())
+        );*/
+
+        Self {
+            s,
+            i_pad: 0,
+            integer,
+            fraction,
+            f_pad: 0,
+            exponent,
+        }
+    }
+
+    fn from_decimal(s: SmallString<[u8; 32]>) -> Result<Self, ()> {
+        let this = Self::new(s);
+        if this.exponent.is_none() {
+            Ok(this)
+        } else {
+            Err(())
+        }
+    }
+
+    fn from_scientific(s: SmallString<[u8; 32]>) -> Result<Self, ()> {
+        let this = Self::new(s);
+        if this.exponent.is_some() {
+            Ok(this)
+        } else {
+            Err(())
+        }
+    }
+
+    fn with_integer_digits(mut self, min: Option<usize>) -> Self {
+        if let Some(min) = min {
+            if min == 0 && &self.s[..self.integer] == "0" {
+                self.integer = 0;
+            } else {
+                self.i_pad = min.saturating_sub(self.integer);
+            }
+        }
+        self
+    }
+
+    fn with_fraction_digits(mut self, min: Option<usize>) -> Self {
+        let min = min.unwrap_or_default();
+        while self.fraction.len() > min && self.s[self.fraction.clone()].ends_with('0') {
+            self.fraction.end -= 1;
+        }
+        self.f_pad = min.saturating_sub(self.fraction.len());
+        self
+    }
+
+    fn integer(&self) -> &str {
+        &self.s[..self.integer]
+    }
+
+    fn fraction(&self) -> &str {
+        &self.s[self.fraction.clone()]
+    }
+
+    fn format(
+        &self,
+        f: &mut std::fmt::Formatter,
+        decimal: char,
+        group: Option<char>,
+    ) -> std::fmt::Result {
+        let i_total = self.i_pad + self.integer;
+        let f_total = self.fraction.len() + self.f_pad;
+        if i_total + f_total == 0 {
+            f.write_char('0')?;
+        } else {
+            if let Some(group) = group {
+                let mut integer = SmallString::<[u8; 16]>::new();
+                integer.extend(repeat_n('0', self.i_pad));
+                integer.push_str(self.integer());
+                for c in group_digits(&integer, group) {
+                    f.write_char(c)?;
+                }
+            } else {
+                for _ in 0..self.i_pad {
+                    f.write_char('0')?;
+                }
+                f.write_str(self.integer())?;
+            }
+            if f_total > 0 {
+                f.write_char(decimal)?;
+                f.write_str(self.fraction())?;
+                for _ in 0..self.f_pad {
+                    f.write_char('0')?;
+                }
+            }
+        }
+        if let Some(exponent) = self.exponent {
+            f.write_str(&self.s[exponent..])?;
+        }
+        Ok(())
+    }
+
+    fn is_nonzero(&self) -> bool {
+        self.integer().bytes().any(|b| b != b'0') || self.fraction().bytes().any(|b| b != b'0')
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{convert::identity, ops::Neg};
+
+    use crate::{
+        data::Datum,
+        spv::read::graph::format::{NegativeLocation, Negatives, NumberFormatter, Scientific},
+    };
+
+    #[test]
+    fn data_number_format_decimal() {
+        fn test<TF, TN>(transform_format: TF, transform_number: TN, expect: &[&str; 12])
+        where
+            TF: Fn(NumberFormatter) -> NumberFormatter,
+            TN: Fn(f64) -> f64,
+        {
+            for ((digits, number), &expectation) in [
+                ((None, None, None, None), 123.0),
+                ((Some(4), None, None, None), 123.0),
+                ((None, Some(2), None, None), 123.0),
+                ((None, None, Some(1), None), 123.0),
+                ((None, None, None, Some(1)), 123.4),
+                ((None, None, None, Some(2)), 123.4),
+                ((None, None, Some(2), None), 123.4),
+                ((None, None, Some(0), None), 0.123),
+                ((Some(0), None, Some(0), None), 0.123),
+                ((Some(0), None, None, Some(1)), 0.123),
+                ((Some(0), None, None, Some(0)), 0.123),
+                ((Some(0), None, None, Some(0)), 0.01),
+            ]
+            .into_iter()
+            .zip(expect)
+            {
+                let (
+                    minimum_integer_digits,
+                    maximum_integer_digits,
+                    minimum_fraction_digits,
+                    maximum_fraction_digits,
+                ) = digits;
+                let format = NumberFormatter::default()
+                    .with_minimum_fraction_digits(minimum_fraction_digits)
+                    .with_maximum_fraction_digits(maximum_fraction_digits)
+                    .with_minimum_integer_digits(minimum_integer_digits)
+                    .with_maximum_integer_digits(maximum_integer_digits);
+                let format = transform_format(format);
+                let number = transform_number(number);
+                assert_eq!(
+                    format.display(&Datum::Number(Some(number))).to_string(),
+                    expectation,
+                    "formatting {number} as {format:?}"
+                );
+            }
+        }
+
+        test(
+            identity,
+            identity,
+            &[
+                "123", "0,123", "*", "123.0", "123.4", "123.4", "123.40", "0.123", ".123", ".1",
+                "0", "0",
+            ],
+        );
+
+        test(
+            |format| format.with_prefix("$").with_suffix("%"),
+            identity,
+            &[
+                "$123%", "$0,123%", "*", "$123.0%", "$123.4%", "$123.4%", "$123.40%", "$0.123%",
+                "$.123%", "$.1%", "$0%", "$0%",
+            ],
+        );
+
+        test(
+            identity,
+            Neg::neg,
+            &[
+                "-123", "-0,123", "*", "-123.0", "-123.4", "-123.4", "-123.40", "-0.123", "-.123",
+                "-.1", "0", "0",
+            ],
+        );
+
+        test(
+            |format| format.with_negatives(Negatives::Absolute),
+            identity,
+            &[
+                "123", "0,123", "*", "123.0", "123.4", "123.4", "123.40", "0.123", ".123", ".1",
+                "0", "0",
+            ],
+        );
+
+        test(
+            |format| format.with_negatives(Negatives::Parentheses),
+            Neg::neg,
+            &[
+                "(123)", "(0,123)", "*", "(123.0)", "(123.4)", "(123.4)", "(123.40)", "(0.123)",
+                "(.123)", "(.1)", "0", "0",
+            ],
+        );
+
+        test(
+            |format| format.with_negatives(Negatives::Parentheses),
+            Neg::neg,
+            &[
+                "(123)", "(0,123)", "*", "(123.0)", "(123.4)", "(123.4)", "(123.40)", "(0.123)",
+                "(.123)", "(.1)", "0", "0",
+            ],
+        );
+
+        test(
+            |format| {
+                format
+                    .with_prefix("$")
+                    .with_suffix("%")
+                    .with_negatives(Negatives::Parentheses)
+            },
+            Neg::neg,
+            &[
+                "$(123)%",
+                "$(0,123)%",
+                "*",
+                "$(123.0)%",
+                "$(123.4)%",
+                "$(123.4)%",
+                "$(123.40)%",
+                "$(0.123)%",
+                "$(.123)%",
+                "$(.1)%",
+                "$0%",
+                "$0%",
+            ],
+        );
+
+        test(
+            |format| {
+                format
+                    .with_prefix("$")
+                    .with_suffix("%")
+                    .with_negative_location(NegativeLocation::Outside)
+            },
+            Neg::neg,
+            &[
+                "-$123%",
+                "-$0,123%",
+                "*",
+                "-$123.0%",
+                "-$123.4%",
+                "-$123.4%",
+                "-$123.40%",
+                "-$0.123%",
+                "-$.123%",
+                "-$.1%",
+                "$0%",
+                "$0%",
+            ],
+        );
+
+        test(
+            |format| {
+                format
+                    .with_prefix("$")
+                    .with_suffix("%")
+                    .with_negatives(Negatives::Parentheses)
+                    .with_negative_location(NegativeLocation::Outside)
+            },
+            Neg::neg,
+            &[
+                "($123%)",
+                "($0,123%)",
+                "*",
+                "($123.0%)",
+                "($123.4%)",
+                "($123.4%)",
+                "($123.40%)",
+                "($0.123%)",
+                "($.123%)",
+                "($.1%)",
+                "$0%",
+                "$0%",
+            ],
+        );
+    }
+
+    #[test]
+    fn data_number_format_scientific() {
+        fn test<TF, TN>(transform_format: TF, transform_number: TN, expect: &[&str; 20])
+        where
+            TF: Fn(NumberFormatter) -> NumberFormatter,
+            TN: Fn(f64) -> f64,
+        {
+            for ((digits, number), &expectation) in [
+                ((None, None), 0.0),
+                ((None, None), 123456.0),
+                ((Some(0), None), 123456.0),
+                ((Some(1), None), 123456.0),
+                ((Some(2), None), 123456.0),
+                ((Some(3), None), 123456.0),
+                ((Some(4), None), 123456.0),
+                ((Some(5), None), 123456.0),
+                ((Some(6), None), 123456.0),
+                ((Some(7), None), 123456.0),
+                ((Some(8), None), 123456.0),
+                ((None, Some(0)), 123456.0),
+                ((None, Some(1)), 123456.0),
+                ((None, Some(2)), 123456.0),
+                ((None, Some(3)), 123456.0),
+                ((None, Some(4)), 123456.0),
+                ((None, Some(5)), 123456.0),
+                ((None, Some(6)), 123456.0),
+                ((None, Some(2)), 0.123456),
+                ((None, Some(2)), 0.0000123456),
+            ]
+            .into_iter()
+            .zip(expect)
+            {
+                let (minimum_fraction_digits, maximum_fraction_digits) = digits;
+                let format = NumberFormatter::default()
+                    .with_scientific(Scientific::True)
+                    .with_minimum_fraction_digits(minimum_fraction_digits)
+                    .with_maximum_fraction_digits(maximum_fraction_digits);
+                let format = transform_format(format);
+                let number = transform_number(number);
+                assert_eq!(
+                    format.display(&Datum::Number(Some(number))).to_string(),
+                    expectation,
+                    "formatting {number} as {format:?}"
+                );
+            }
+        }
+
+        test(
+            identity,
+            identity,
+            &[
+                "0",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.234560E5",
+                "1.2345600E5",
+                "1.23456000E5",
+                "1E5",
+                "1.2E5",
+                "1.23E5",
+                "1.235E5",
+                "1.2346E5",
+                "1.23456E5",
+                "1.23456E5",
+                "1.23E-1",
+                "1.23E-5",
+            ],
+        );
+
+        test(
+            identity,
+            Neg::neg,
+            &[
+                "0",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.234560E5",
+                "-1.2345600E5",
+                "-1.23456000E5",
+                "-1E5",
+                "-1.2E5",
+                "-1.23E5",
+                "-1.235E5",
+                "-1.2346E5",
+                "-1.23456E5",
+                "-1.23456E5",
+                "-1.23E-1",
+                "-1.23E-5",
+            ],
+        );
+
+        test(
+            |format| format.with_negatives(Negatives::Parentheses),
+            Neg::neg,
+            &[
+                "0",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.234560E5)",
+                "(1.2345600E5)",
+                "(1.23456000E5)",
+                "(1E5)",
+                "(1.2E5)",
+                "(1.23E5)",
+                "(1.235E5)",
+                "(1.2346E5)",
+                "(1.23456E5)",
+                "(1.23456E5)",
+                "(1.23E-1)",
+                "(1.23E-5)",
+            ],
+        );
+
+        test(
+            |format| {
+                format
+                    .with_negatives(Negatives::Parentheses)
+                    .with_prefix("[")
+                    .with_suffix("]")
+            },
+            Neg::neg,
+            &[
+                "[0]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.234560E5)]",
+                "[(1.2345600E5)]",
+                "[(1.23456000E5)]",
+                "[(1E5)]",
+                "[(1.2E5)]",
+                "[(1.23E5)]",
+                "[(1.235E5)]",
+                "[(1.2346E5)]",
+                "[(1.23456E5)]",
+                "[(1.23456E5)]",
+                "[(1.23E-1)]",
+                "[(1.23E-5)]",
+            ],
+        );
+
+        test(
+            |format| {
+                format
+                    .with_negatives(Negatives::Parentheses)
+                    .with_negative_location(NegativeLocation::Outside)
+                    .with_prefix("[")
+                    .with_suffix("]")
+            },
+            Neg::neg,
+            &[
+                "[0]",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.234560E5])",
+                "([1.2345600E5])",
+                "([1.23456000E5])",
+                "([1E5])",
+                "([1.2E5])",
+                "([1.23E5])",
+                "([1.235E5])",
+                "([1.2346E5])",
+                "([1.23456E5])",
+                "([1.23456E5])",
+                "([1.23E-1])",
+                "([1.23E-5])",
+            ],
+        );
+    }
+}
diff --git a/rust/pspp/src/spv/read/graph/frames.rs b/rust/pspp/src/spv/read/graph/frames.rs
new file mode 100644 (file)
index 0000000..c777e65
--- /dev/null
@@ -0,0 +1,976 @@
+use std::{collections::HashMap, fmt::Debug, iter::zip, ops::Range};
+
+use cairo::{Context, RecordingSurface};
+use enum_iterator::all;
+use enum_map::{Enum, EnumMap, enum_map};
+use svg::{
+    Node as _,
+    node::element::{Group, Rectangle, Title},
+};
+
+use crate::{
+    output::pivot::Axis2,
+    spv::read::graph::{
+        AnyFrame, Container, Frame, Graph, LabelFrame, Legend, Location, Location2, LocationLength,
+        Span, Visualization,
+        frames::simplex::{Point, minimize},
+    },
+};
+
+impl Visualization {
+    pub fn decode_frames<'a>(&'a self) -> Frames<'a> {
+        let mut frames = vec![Frame::new(FrameType::Visualization(self), None)];
+        let mut ids = HashMap::new();
+        fn decode_container<'a>(
+            parent: usize,
+            container: &'a Container,
+            frames: &mut Vec<Frame<'a>>,
+            ids: &mut HashMap<&'a String, usize>,
+        ) {
+            let container_index = frames.len();
+            frames[parent].children.push(container_index);
+            if let Some(id) = &container.id {
+                ids.insert(id, container_index);
+            }
+            frames.push(Frame::new(FrameType::Container(container), Some(parent)));
+            for child in &container.children {
+                match child {
+                    AnyFrame::Graph(graph) => {
+                        let index = frames.len();
+                        frames[container_index].children.push(index);
+                        if let Some(id) = &graph.id {
+                            ids.insert(id, index);
+                        }
+                        frames.push(Frame::new(FrameType::Graph(graph), Some(container_index)));
+                    }
+                    AnyFrame::LabelFrame(label_frame) => {
+                        let index = frames.len();
+                        frames[container_index].children.push(index);
+                        if let Some(id) = &label_frame.id {
+                            ids.insert(id, index);
+                        }
+                        frames.push(Frame::new(
+                            FrameType::LabelFrame(label_frame),
+                            Some(container_index),
+                        ));
+                    }
+                    AnyFrame::Legend(legend) => {
+                        let index = frames.len();
+                        frames[container_index].children.push(index);
+                        if let Some(id) = &legend.id {
+                            ids.insert(id, index);
+                        }
+                        frames.push(Frame::new(FrameType::Legend(legend), Some(container_index)));
+                    }
+                    AnyFrame::Container(container) => {
+                        decode_container(container_index, container, frames, ids)
+                    }
+                }
+            }
+        }
+
+        for container in &self.children {
+            match container {
+                AnyFrame::Graph(graph) => {
+                    let index = frames.len();
+                    frames[0].children.push(index);
+                    if let Some(id) = &graph.id {
+                        ids.insert(id, index);
+                    }
+                    frames.push(Frame::new(FrameType::Graph(graph), Some(0)));
+                }
+                AnyFrame::LabelFrame(label_frame) => {
+                    let index = frames.len();
+                    frames[0].children.push(index);
+                    if let Some(id) = &label_frame.id {
+                        ids.insert(id, index);
+                    }
+                    frames.push(Frame::new(FrameType::LabelFrame(label_frame), Some(0)));
+                }
+                AnyFrame::Container(container) => {
+                    decode_container(0, container, &mut frames, &mut ids)
+                }
+                AnyFrame::Legend(_) => todo!(),
+            }
+        }
+
+        for frame in &mut frames {
+            for location in frame.frame_type.locations() {
+                frame.locations[location.axis()][location.component()] = location.decode(&ids);
+            }
+        }
+
+        let mut frames = Frames::new(frames);
+        frames.set_fixed_size(enum_map! {Axis2::X => 400.0, Axis2::Y => 300.0});
+        frames
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum FrameType<'a> {
+    Visualization(&'a Visualization),
+    Container(&'a Container),
+    Graph(&'a Graph),
+    LabelFrame(&'a LabelFrame),
+    Legend(&'a Legend),
+}
+
+impl<'a> FrameType<'a> {
+    fn id(&self) -> &Option<String> {
+        match self {
+            FrameType::Visualization(visualization) => &visualization.id,
+            FrameType::Container(container) => &container.id,
+            FrameType::Graph(graph) => &graph.id,
+            FrameType::LabelFrame(label_frame) => &label_frame.id,
+            FrameType::Legend(legend) => &legend.id,
+        }
+    }
+
+    fn preferred_size(&self, max: &EnumMap<Axis2, Option<f64>>) -> EnumMap<Axis2, f64> {
+        let default = match self {
+            FrameType::Visualization(_) | FrameType::Container(_) => EnumMap::default(),
+            FrameType::Graph(_) => {
+                enum_map! { Axis2::X => 400.0, Axis2::Y => 300.0 }
+            }
+            FrameType::LabelFrame(label_frame) => {
+                let surface = RecordingSurface::create(cairo::Content::ColorAlpha, None).unwrap();
+                let context = Context::new(&surface).unwrap();
+                let pangocairo_context = pangocairo::functions::create_context(&context);
+                pangocairo::functions::context_set_resolution(&pangocairo_context, 96.0);
+                let layout = pango::Layout::new(&pangocairo_context);
+                let text = label_frame.text().unwrap_or_default();
+                layout.set_text(&text);
+                let (w, h) = layout.size();
+                enum_map! { Axis2::X => w as f64 / 1024.0, Axis2::Y => h as f64 / 1024.0}
+            }
+            FrameType::Legend(_) => enum_map! { Axis2::X => 100.0, Axis2::Y => 200.0 },
+        };
+        zip(default, max.into_values())
+            .map(|((axis, default), max)| {
+                (
+                    axis,
+                    if let Some(max) = max
+                        && default > max
+                    {
+                        max
+                    } else {
+                        default
+                    },
+                )
+            })
+            .collect()
+    }
+
+    fn can_contain_children(&self) -> bool {
+        match self {
+            FrameType::Visualization(_) | FrameType::Container(_) => true,
+            FrameType::Graph(_) | FrameType::LabelFrame(_) | FrameType::Legend(_) => false,
+        }
+    }
+
+    pub fn locations(&self) -> &[Location] {
+        match self {
+            FrameType::Visualization(visualization) => &visualization.locations,
+            FrameType::Container(container) => &container.locations,
+            FrameType::Graph(graph) => &graph.locations,
+            FrameType::LabelFrame(label_frame) => &label_frame.locations,
+            FrameType::Legend(legend) => &legend.locations,
+        }
+    }
+
+    pub fn weight(&self) -> f64 {
+        match self {
+            FrameType::Visualization(_) => 1.0,
+            FrameType::Container(_) => 1.0,
+            FrameType::Graph(_) => 100.0,
+            FrameType::LabelFrame(_) => 4.0,
+            FrameType::Legend(_) => 10.0,
+        }
+    }
+
+    pub fn as_label_frame(&self) -> Option<&LabelFrame> {
+        match self {
+            FrameType::LabelFrame(label_frame) => Some(label_frame),
+            _ => None,
+        }
+    }
+}
+
+pub struct Frames<'a> {
+    pub frames: Vec<Frame<'a>>,
+}
+
+impl<'a> Frames<'a> {
+    fn get_value(
+        &self,
+        frame: &Frame,
+        location: &Location2,
+        _parent_start: Option<f64>,
+        parent_length: Option<f64>,
+        strict: bool,
+    ) -> Option<f64> {
+        match location {
+            Location2::Fixed { length, min, max } => {
+                let mut value = length.as_pixels(parent_length)?;
+                if let Some(min) = &min {
+                    value = value.min(min.as_pixels(parent_length)?);
+                }
+                if let Some(max) = &max {
+                    value = value.max(max.as_pixels(parent_length)?);
+                }
+                Some(value)
+            }
+            Location2::Equals {
+                axis,
+                component,
+                index,
+            } => self.frames[*index].position[*axis].values[*component],
+            Location2::SizeToContent { axis, .. } => self.preferred_size(frame, strict)[*axis],
+        }
+    }
+
+    fn are_children_ordered(&self, frame: &Frame, axis: Axis2) -> bool {
+        frame.children.len() < 2 || {
+            let n_attachments = frame
+                .children(&self.frames)
+                .map(|child| {
+                    let start_attached = child.is_attached(axis, Component::Start);
+                    let end_attached = child.is_attached(axis, Component::End);
+                    (start_attached as usize) + (end_attached as usize)
+                })
+                .sum::<usize>();
+            n_attachments == frame.children.len() - 1
+        }
+    }
+
+    fn child_axis_size(&self, frame: &Frame, axis: Axis2, strict: bool) -> Option<f64> {
+        #[derive(Default)]
+        struct Span(Option<Range<f64>>);
+
+        impl Span {
+            fn add_point(&mut self, point: f64) {
+                if let Some(range) = &mut self.0 {
+                    if point < range.start {
+                        range.start = point;
+                    } else if point > range.end {
+                        range.end = point;
+                    }
+                } else {
+                    self.0 = Some(point..point);
+                }
+            }
+
+            fn add_range(&mut self, new: Range<f64>) {
+                if let Some(range) = &mut self.0 {
+                    if new.start < range.start {
+                        range.start = new.start;
+                    }
+                    if new.end > range.end {
+                        range.end = new.end;
+                    }
+                } else {
+                    self.0 = Some(new);
+                }
+            }
+        }
+
+        let mut span = Span::default();
+        let ordered = self.are_children_ordered(frame, axis);
+        let mut width = 0.0;
+        for child in frame.children(&self.frames) {
+            let start = child.position[axis].start();
+            let end = child.position[axis].end();
+            let length = child.position[axis].length();
+            if let Some(length) = length {
+                width += length;
+            }
+            if strict {
+                let Some(length) = length else {
+                    return None;
+                };
+                if let Some(start) = start
+                    && let Some(end) = end
+                {
+                    span.add_point(start);
+                    span.add_point(end);
+                } else if !ordered {
+                    span.add_range(0.0..length)
+                }
+            } else {
+                span.add_point(end.unwrap_or_default());
+                if let Some(end) = end {
+                    span.add_point(end);
+                } else if let Some(length) = length {
+                    span.add_point(length);
+                } else if let Some(size) = self.preferred_size(child, true)[axis] {
+                    span.add_point(size);
+                }
+            }
+
+            if let Some(start) = start
+                && let Some(end) = end
+                && let Some(child_start) = &child.locations[axis][Component::Start]
+                && let Location2::Fixed { length, .. } = child_start
+                && let LocationLength::Percent(percent) = *length
+            {
+                let fraction = percent / 100.0;
+                span.add_point(start - (end - start) * fraction / (1.0 - fraction));
+            }
+        }
+        if ordered {
+            Some(width + span.0.map_or(0.0, |range| range.start))
+        } else {
+            Some(span.0.map_or(0.0, |range| range.end))
+        }
+    }
+
+    fn child_size(&self, frame: &Frame, strict: bool) -> EnumMap<Axis2, Option<f64>> {
+        EnumMap::from_fn(|axis| self.child_axis_size(frame, axis, strict))
+    }
+
+    pub fn preferred_size(&self, frame: &Frame, strict: bool) -> EnumMap<Axis2, Option<f64>> {
+        // XXX if the frame is invisible, return zeros.
+        let max = EnumMap::from_fn(|axis| self.max_length(frame, axis));
+        if frame.frame_type.can_contain_children() {
+            self.child_size(frame, strict)
+                .map(|_, length| length.unwrap_or_default() /*XXX*/)
+        } else {
+            frame.frame_type.preferred_size(&max)
+        }
+        .map(|axis, mut size| {
+            if let Some(location) = &frame.locations[axis][Component::Length] {
+                let parent_length = frame
+                    .parent
+                    .and_then(|parent| self.frames[parent].position[axis].length());
+                if let Some(length) = location.as_fixed()
+                    && let Some(length) = length.as_pixels(parent_length)
+                {
+                    size = length;
+                }
+                if let Some(max) = location.max()
+                    && let Some(max) = max.as_pixels(parent_length)
+                    && max < size
+                {
+                    size = max;
+                }
+            }
+            Some(size)
+        })
+    }
+
+    fn max_length(&'a self, mut frame: &'a Frame<'a>, axis: Axis2) -> Option<f64> {
+        loop {
+            if let Some(length) = frame.position[axis].length() {
+                return Some(length);
+            }
+            frame = &self.frames[frame.parent?];
+        }
+    }
+
+    fn frame_updated_position(
+        &self,
+        index: usize,
+        parent: &EnumMap<Axis2, Position>,
+        strict: bool,
+    ) -> EnumMap<Axis2, Position> {
+        let frame = &self.frames[index];
+        let mut vec = Vec::new();
+        for (axis, locations) in &frame.locations {
+            for (component, location) in locations {
+                if let Some(location) = location {
+                    vec.push((axis, component, location));
+                }
+            }
+        }
+        vec.sort_unstable_by_key(|(_, _, location)| location.sort_key());
+
+        let mut new_position = frame.position.clone();
+        for (axis, component, location) in vec {
+            let parent_start = parent[axis].start();
+            let parent_length = parent[axis].length();
+            if let Some(value) =
+                self.get_value(frame, &location, parent_start, parent_length, strict)
+            {
+                let value = if let Some(parent_length) = parent_length
+                    && component == Component::Length
+                    && frame.frame_type.can_contain_children()
+                {
+                    value.min(parent_length)
+                } else {
+                    value
+                };
+                new_position[axis].set(component, value);
+            }
+        }
+
+        for (axis, position) in &mut new_position {
+            position.assign_default_position(&self.frames, index, axis, &parent[axis]);
+        }
+        new_position
+    }
+
+    fn missing_values(&self) -> usize {
+        self.frames.iter().map(|frame| frame.missing_values()).sum()
+    }
+
+    fn update_round(&mut self) {
+        for index in 1..self.frames.len() {
+            self.frames[index].position = self.frame_updated_position(
+                index,
+                &self.frames[self.frames[index].parent.unwrap()].position,
+                true,
+            );
+        }
+    }
+
+    pub fn new(frames: Vec<Frame<'a>>) -> Self {
+        Self { frames }
+    }
+
+    pub fn set_fixed_size(&mut self, size: EnumMap<Axis2, f64>) {
+        let mut position: EnumMap<Axis2, Position> = EnumMap::default();
+        for (axis, default) in size {
+            if self.frames[0].locations[axis][Component::Length]
+                .as_ref()
+                .is_none_or(|location| location.is_size_to_content())
+            {
+                position[axis].set_start(0.0);
+                position[axis].set_end(default);
+            }
+        }
+        self.frames[0].position = self.frame_updated_position(0, &position, true);
+
+        let mut strict = true;
+        while let n = self.missing_values()
+            && n > 0
+        {
+            self.update_round();
+            if self.missing_values() == n {
+                if !strict {
+                    break;
+                }
+                strict = false;
+                let size = self.preferred_size(&self.frames[0], false);
+                for (axis, position) in &mut self.frames[0].position {
+                    if position.start().is_none() {
+                        position.set_start(0.0);
+                    }
+                    if position.length().is_none() {
+                        position.set_length(size[axis].unwrap_or_default());
+                    }
+                }
+            }
+        }
+    }
+
+    fn set_to_preferred(&mut self) -> bool {
+        self.frames[0].position = self.frame_updated_position(0, &EnumMap::default(), true);
+        self.update_round();
+        for index in 0..self.frames.len() {
+            let mut preferred_size = None;
+            for axis in all::<Axis2>() {
+                if self.frames[index].position[axis].length().is_none() {
+                    if let Some(length) = preferred_size
+                        .get_or_insert_with(|| self.preferred_size(&self.frames[index], true))[axis]
+                    {
+                        self.frames[index].position[axis].set_length(length);
+                    }
+                }
+            }
+        }
+
+        if let Some(width) = self.frames[0].position[Axis2::X].length()
+            && let Some(height) = self.frames[0].position[Axis2::Y].length()
+        {
+            for frame in &mut self.frames {
+                frame.position.clear();
+            }
+            self.set_fixed_size(enum_map! {Axis2::X => width, Axis2::Y => height});
+            true
+        } else {
+            false
+        }
+    }
+
+    fn set_simplex(&mut self) {
+        let start = if let Some(frame) = self
+            .frames
+            .iter()
+            .find(|frame| matches!(&frame.frame_type, FrameType::Graph(_)))
+        {
+            let size = frame.frame_type.preferred_size(&EnumMap::default());
+            let max = enum_map! { Axis2::X => 60.0, Axis2::Y => 40.0 };
+            size.map(|axis, value| value.max(max[axis]))
+        } else {
+            enum_map! { Axis2::X => 400.0, Axis2::Y => 300.0}
+        };
+        let root_size = self.root_size();
+        let start = EnumMap::from_fn(|axis| root_size[axis].unwrap_or(start[axis])).into_array();
+        let step = start.clone().map(|value| (value / 8.0).min(10.0));
+        let size = minimize(Point(start), Point(step), 1.0, |size| {
+            self.demerits(EnumMap::from_array(size.0))
+        })
+        .unwrap_or(Point([400.0, 300.0]));
+        self.set_fixed_size(EnumMap::from_array(size.0));
+    }
+
+    fn root_size(&self) -> EnumMap<Axis2, Option<f64>> {
+        EnumMap::from_fn(|axis| {
+            self.frames[0].locations[axis][Component::Length]
+                .as_ref()
+                .and_then(|location| location.as_fixed())
+                .and_then(|fixed| fixed.as_pixels(None))
+        })
+    }
+
+    fn root_size_full(&self) -> Option<EnumMap<Axis2, f64>> {
+        let mut size = EnumMap::default();
+        for (axis, length) in &mut size {
+            if let Some(location) = &self.frames[0].locations[axis][Component::Length]
+                && let Some(fixed) = location.as_fixed()
+                && let Some(pixels) = fixed.as_pixels(None)
+            {
+                *length = pixels;
+            } else {
+                return None;
+            }
+        }
+        Some(size)
+    }
+
+    pub fn set_preferred_size(&mut self) {
+        if let Some(root_size) = self.root_size_full() {
+            self.set_fixed_size(root_size);
+        } else if !self.set_to_preferred() {
+            self.set_simplex();
+        }
+    }
+
+    pub fn fit_demerits(&mut self, size: EnumMap<Axis2, f64>) -> f64 {
+        if size.values().copied().any(|length| length < 10.0) {
+            f64::INFINITY
+        } else {
+            // XXX clear previous
+            self.set_fixed_size(size);
+            self.demerits(size) + (size[Axis2::X] * size[Axis2::Y]).sqrt() / 100.0
+        }
+    }
+
+    pub fn demerits(&self, _size: EnumMap<Axis2, f64>) -> f64 {
+        todo!()
+    }
+
+    pub(super) fn graphs(&self) -> impl Iterator<Item = (&Frame<'_>, &Graph)> {
+        self.frames
+            .iter()
+            .filter_map(|frame| match &frame.frame_type {
+                FrameType::Graph(graph) => Some((frame, *graph)),
+                _ => None,
+            })
+    }
+
+    pub fn to_svg(&self) -> Group {
+        let mut group = Group::new();
+        for frame in &self.frames {
+            if let Some(left) = frame.position[Axis2::X].start()
+                && let Some(width) = frame.position[Axis2::X].length()
+                && let Some(top) = frame.position[Axis2::Y].start()
+                && let Some(height) = frame.position[Axis2::Y].length()
+            {
+                let mut rectangle = Rectangle::new()
+                    .set("x", left)
+                    .set("y", top)
+                    .set("width", width)
+                    .set("height", height)
+                    .set("fill", "none")
+                    .set("stroke", "red");
+                if let Some(id) = frame.frame_type.id() {
+                    rectangle.append(Title::new(id));
+                }
+                group.append(rectangle);
+            }
+        }
+        group
+    }
+}
+
+/// Position.
+#[derive(Clone, Debug, Default)]
+pub struct Position {
+    values: EnumMap<Component, Option<f64>>,
+    error: f64,
+}
+
+impl Position {
+    pub fn start(&self) -> Option<f64> {
+        self.values[Component::Start]
+    }
+    pub fn end(&self) -> Option<f64> {
+        self.values[Component::End]
+    }
+    pub fn length(&self) -> Option<f64> {
+        self.values[Component::Length]
+    }
+    fn set_start(&mut self, value: f64) {
+        self.set(Component::Start, value);
+    }
+    fn set_end(&mut self, value: f64) {
+        self.set(Component::End, value);
+    }
+    fn set_length(&mut self, value: f64) {
+        self.set(Component::Length, value);
+    }
+    pub fn span(&self) -> Option<Span> {
+        self.start()
+            .zip(self.end())
+            .map(|(start, end)| Span(start, end))
+    }
+    pub fn missing_values(&self) -> usize {
+        self.values.values().filter(|value| value.is_none()).count()
+    }
+    pub fn demerits(&self) -> f64 {
+        self.error.abs()
+    }
+    fn set(&mut self, component: Component, value: f64) {
+        self.values[component] = Some(value);
+        match (self.start(), self.end(), self.length()) {
+            (Some(start), Some(end), Some(length)) => {
+                self.error = length - (end - start);
+                if self.error > 0.0 {
+                    self.values[Component::Length] = Some(end - start);
+                } else {
+                    self.values[Component::End] = Some(start + length);
+                }
+            }
+            (Some(start), Some(end), None) => {
+                self.values[Component::Length] = Some(end - start);
+            }
+            (Some(start), None, Some(length)) => {
+                self.values[Component::End] = Some(start + length);
+            }
+            (None, Some(end), Some(length)) => {
+                self.values[Component::Start] = Some(end - length);
+            }
+            (None, None, None) => unreachable!(),
+            (Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => (),
+        }
+    }
+
+    fn assign_default_position(
+        &mut self,
+        frames: &[Frame],
+        index: usize,
+        axis: Axis2,
+        parent: &Position,
+    ) {
+        /// Returns true if any frame in `frames` is attached to `target_index`
+        /// on `axis` and `component`.
+        fn is_attached(
+            frames: &[Frame],
+            target_index: usize,
+            axis: Axis2,
+            target_component: Component,
+        ) -> bool {
+            for frame in frames {
+                if let Some(location) = &frame.locations[axis][target_component]
+                    && let Location2::Equals {
+                        index, component, ..
+                    } = location
+                    && *index == target_index
+                    && *component == target_component.opposite().unwrap()
+                {
+                    return true;
+                }
+            }
+            false
+        }
+
+        let frame = &frames[index];
+        if self.start().is_some() && self.end().is_some() {
+            return;
+        }
+
+        let unknown_start = frame.locations[axis][Component::Start].is_none();
+        let unknown_end = frame.locations[axis][Component::End].is_none();
+        let unknown_length =
+            frame.locations[axis][Component::Length].is_none() && self.length().is_none();
+        match (unknown_start, unknown_end, unknown_length) {
+            (true, true, true) => {
+                *self = parent.clone();
+            }
+            (true, true, false) => {
+                if let Some(parent_start) = parent.start()
+                    && is_attached(frames, index, axis, Component::Start)
+                {
+                    // We know where the parent starts, and another frame is
+                    // attached to our end, so start us where our parent starts.
+                    self.set(Component::Start, parent_start);
+                } else if let Some(parent_end) = parent.end()
+                    && is_attached(frames, index, axis, Component::End)
+                {
+                    // We know where the parent ends, and another frame is
+                    // attached to our start, so end us where our parent ends.
+                    self.set(Component::End, parent_end);
+                } else if let Some(length) = self.length()
+                    && let Some(parent_length) = parent.length()
+                    && let Some(parent_start) = parent.start()
+                {
+                    // We have our parent's location, and no other frame is
+                    // before or after us, so center us within the parent.
+                    self.set_start(parent_start + parent_length / 2.0 - length / 2.0);
+                }
+            }
+            (true, false, true) => {
+                if let Some(parent_start) = parent.start() {
+                    self.set(Component::Start, parent_start);
+                }
+            }
+            (false, true, true) => {
+                if let Some(parent_end) = parent.end() {
+                    self.set(Component::End, parent_end);
+                }
+            }
+            (false, false, false)
+            | (false, false, true)
+            | (true, false, false)
+            | (false, true, false) => (),
+        }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Enum)]
+pub enum Component {
+    Start,
+    End,
+    Length,
+}
+
+impl Component {
+    pub fn opposite(&self) -> Option<Self> {
+        match self {
+            Component::Start => Some(Self::End),
+            Component::End => Some(Self::Start),
+            Component::Length => None,
+        }
+    }
+}
+
+mod simplex {
+    use std::{
+        f64::consts::SQRT_2,
+        ops::{Add, Div, Mul, Sub},
+    };
+
+    use ordered_float::OrderedFloat;
+
+    /// Minimization failed to converge.
+    #[derive(Debug)]
+    pub struct Nonconverging;
+
+    fn square(x: f64) -> f64 {
+        x * x
+    }
+    pub fn minimize(
+        start: Point,
+        step: Point,
+        tolerance: f64,
+        f: impl Fn(Point) -> f64,
+    ) -> Result<Point, Nonconverging> {
+        let result = Simplex::with_start_and_step(start, step, &f).iterate(tolerance)?;
+        let result = Simplex::with_start_and_step(result, step / 5.0, &f).iterate(tolerance)?;
+        Ok(result)
+    }
+
+    #[derive(Copy, Clone, Debug)]
+    struct Candidate {
+        size: Point,
+        score: f64,
+    }
+
+    impl Candidate {
+        fn new(size: Point, f: impl Fn(Point) -> f64) -> Self {
+            Self {
+                size,
+                score: f(size),
+            }
+        }
+    }
+
+    struct Simplex<F> {
+        candidates: [Candidate; 3],
+        score: F,
+    }
+
+    impl<F> Simplex<F>
+    where
+        F: Fn(Point) -> f64,
+    {
+        fn with_start_and_step(start: Point, step: Point, score: F) -> Self {
+            let step = step / SQRT_2;
+            Self::new(
+                [start, start + step, start + Point([-step.0[0], step.0[1]])],
+                score,
+            )
+        }
+        fn new(initial: [Point; 3], score: F) -> Self {
+            let mut candidates = initial.map(|point| Candidate::new(point, &score));
+            candidates.sort_by_key(|candidate| OrderedFloat(candidate.score));
+            Self { candidates, score }
+        }
+        fn step(&mut self) {
+            let [min, mid, max] = self.candidates;
+
+            const ALPHA: f64 = 1.0;
+            const GAMMA: f64 = 2.0;
+            const RHO: f64 = 0.5;
+            const SIGMA: f64 = 0.5;
+
+            let xo = Candidate::new(min.size.midpoint(mid.size), &self.score);
+            let xr = Candidate::new(xo.size + (xo.size - max.size) * ALPHA, &self.score);
+            let new = if xr.score < min.score {
+                // Expansion
+                let xe = Candidate::new(xo.size + (xr.size - xo.size) * GAMMA, &self.score);
+                Some(if xe.score < xr.score { xe } else { xr })
+            } else if xr.score < mid.score {
+                // Reflection
+                Some(xr)
+            } else if xr.score < max.score {
+                // Contraction case 1
+                let xc = Candidate::new(xo.size + (xr.size - xo.size) * RHO, &self.score);
+                (xc.score < xr.score).then_some(xc)
+            } else {
+                // Contraction case 2
+                let xc = Candidate::new(xo.size + (max.size - xo.size) * RHO, &self.score);
+                (xc.score < max.score).then_some(xc)
+            };
+            let mut candidates = if let Some(new) = new {
+                [min, mid, new]
+            } else {
+                // Shrink.
+                [
+                    Candidate::new(min.size + (mid.size - min.size) * SIGMA, &self.score),
+                    Candidate::new(min.size + (max.size - min.size) * SIGMA, &self.score),
+                    min,
+                ]
+            };
+            candidates.sort_by_key(|candidate| OrderedFloat(candidate.score));
+            self.candidates = candidates;
+        }
+        /// Returns the minimum and the number of iterations used to arrive at
+        /// it.
+        fn iterate_full(mut self, tolerance: f64) -> Result<(Point, usize), Nonconverging> {
+            for n in 0..5000 {
+                if self.stddev() < tolerance {
+                    return Ok((self.candidates[0].size, n));
+                }
+                self.step();
+            }
+            Err(Nonconverging)
+        }
+
+        /// Returns the minimum.
+        fn iterate(self, tolerance: f64) -> Result<Point, Nonconverging> {
+            self.iterate_full(tolerance).map(|(minimum, _n)| minimum)
+        }
+    }
+    impl<F> Simplex<F> {
+        fn scores(&self) -> impl ExactSizeIterator<Item = f64> {
+            self.candidates.iter().map(|candidate| candidate.score)
+        }
+        fn stddev(&self) -> f64 {
+            let n = self.scores().len() as f64;
+            let mean = self.scores().sum::<f64>() / n;
+            let variance = self.scores().map(|score| square(score - mean)).sum::<f64>() / n;
+            variance.sqrt()
+        }
+    }
+
+    #[derive(Copy, Clone, Debug)]
+    pub struct Point(pub [f64; 2]);
+
+    impl Point {
+        #[cfg(test)]
+        pub fn new(x: f64, y: f64) -> Self {
+            Self([x, y])
+        }
+
+        pub fn midpoint(self, other: Self) -> Self {
+            Self([
+                self.0[0].midpoint(other.0[0]),
+                self.0[1].midpoint(other.0[1]),
+            ])
+        }
+
+        #[cfg(test)]
+        pub fn distance(self, other: Self) -> f64 {
+            let dx = self.0[0] - other.0[0];
+            let dy = self.0[1] - other.0[1];
+            dx.hypot(dy)
+        }
+    }
+
+    impl Mul<f64> for Point {
+        type Output = Point;
+
+        fn mul(self, rhs: f64) -> Self::Output {
+            Self([self.0[0] * rhs, self.0[1] * rhs])
+        }
+    }
+
+    impl Div<f64> for Point {
+        type Output = Point;
+
+        fn div(self, rhs: f64) -> Self::Output {
+            Self([self.0[0] / rhs, self.0[1] / rhs])
+        }
+    }
+
+    impl Add<Point> for Point {
+        type Output = Point;
+
+        fn add(self, rhs: Point) -> Self::Output {
+            Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
+        }
+    }
+
+    impl Sub<Point> for Point {
+        type Output = Point;
+
+        fn sub(self, rhs: Point) -> Self::Output {
+            Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
+        }
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use crate::spv::read::graph::frames::simplex::{Point, Simplex, square};
+
+        /// Tests the minimizer for the Rosenbrock "banana" function, suggested
+        /// at <https://www.brnt.eu/phd/node10.html>.  The function's minimum is
+        /// at (1,1).
+        #[test]
+        fn banana() {
+            const ALPHA: f64 = 10.0;
+
+            fn score(point: Point) -> f64 {
+                let [x1, x2] = point.0;
+                square(1.0 - x1) + ALPHA * square(x2 - square(x1))
+            }
+
+            let simplex = Simplex::new(
+                [
+                    Point::new(-1.0, 1.5),
+                    Point::new(0.25, 1.0),
+                    Point::new(-1.0, -0.5),
+                ],
+                score,
+            );
+            let (minimum, n) = simplex.iterate_full(0.0001).unwrap();
+            let distance = minimum.distance(Point::new(1.0, 1.0));
+            assert!(
+                (25..50).contains(&n) && distance < 0.01,
+                "{minimum:?} is distance {distance} from (1,1) after {n} iterations"
+            );
+        }
+    }
+}
diff --git a/rust/pspp/src/spv/read/graph/patterns.rs b/rust/pspp/src/spv/read/graph/patterns.rs
new file mode 100644 (file)
index 0000000..3574557
--- /dev/null
@@ -0,0 +1,116 @@
+use std::{collections::BTreeSet, fmt::Display};
+
+use svg::{
+    Node,
+    node::{Blob, element::Rectangle},
+};
+
+use crate::output::pivot::look::Color;
+
+mod data;
+
+pub struct Pattern {
+    width: f64,
+    height: f64,
+    attributes: &'static [(&'static str, &'static str)],
+    content: &'static str,
+}
+
+impl Pattern {
+    /// Returns the pattern with the given `index`, or `None` if the index is
+    /// greater than or equal to [Pattern::N].
+    pub fn for_index(index: usize) -> Option<&'static Self> {
+        data::PATTERNS.get(index)
+    }
+
+    pub fn to_svg(&self, color: Color, color2: Color) -> svg::node::element::Pattern {
+        let mut group = svg::node::element::Pattern::new()
+            .set("fill", color.display_css().to_string())
+            .set("width", self.width)
+            .set("height", self.height)
+            .add(
+                Rectangle::new()
+                    .set("x", 0)
+                    .set("y", 0)
+                    .set("width", self.width)
+                    .set("height", self.height)
+                    .set("fill", color2.display_css().to_string()),
+            )
+            .add(Blob::new(self.content));
+        for (name, value) in self.attributes.into_iter().copied() {
+            group.assign(name, value);
+        }
+        group
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ColoredPattern {
+    pub index: usize,
+    pub color: Color,
+    pub color2: Color,
+}
+
+impl Display for ColoredPattern {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "pattern{}_{}_{}",
+            self.index,
+            self.color.display_hex(),
+            self.color2.display_hex()
+        )?;
+        Ok(())
+    }
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct Patterns {
+    instances: BTreeSet<ColoredPattern>,
+}
+
+impl Patterns {
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    pub fn insert(&mut self, p: ColoredPattern) -> String {
+        if p.index != 0 && Pattern::for_index(p.index).is_some() {
+            self.instances.insert(p);
+            format!("url(#{p})")
+        } else {
+            p.color.display_css().to_string()
+        }
+    }
+
+    pub fn to_svg(&self) -> impl Iterator<Item = svg::node::element::Pattern> {
+        self.instances.iter().map(|cp| {
+            Pattern::for_index(cp.index)
+                .unwrap()
+                .to_svg(cp.color, cp.color2)
+                .set("id", cp.to_string())
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{output::pivot::look::Color, spv::read::graph::patterns::Pattern};
+
+    #[test]
+    fn pattern() {
+        let pattern = Pattern::for_index(1).unwrap();
+        let svg = pattern.to_svg(Color::BLACK, Color::WHITE).to_string();
+        println!("{svg}");
+        assert_eq!(
+            svg,
+            r##"<pattern fill="#000000" height="4" patternUnits="userSpaceOnUse" width="10">
+<rect fill="#ffffff" height="4" width="10" x="0" y="0"/>
+
+      <circle cx="4.5" cy="3.5" r="0.5" height="1" />
+      <rect x="9" y="1" width="1" height="1" />
+    
+</pattern>"##
+        );
+    }
+}
diff --git a/rust/pspp/src/spv/read/graph/patterns.svg b/rust/pspp/src/spv/read/graph/patterns.svg
new file mode 100644 (file)
index 0000000..e8ceef0
--- /dev/null
@@ -0,0 +1,155 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <!--
+        An SVG <pattern> for each of the numbered patterns supported by VizML.
+        The pattern numbered `<n>` has id="pattern<n>".
+
+        SVG provides no way to parameterize patterns, so all of these are
+        designed to be recolorable by changing their `fill` attribute.  PSPP
+        instantiates a copy of a given pattern with a different `fill` for each
+        colored pattern it needs.
+    -->
+    <pattern id="pattern0" gifindex="0" width="1" height="1" patternUnits="userSpaceOnUse"/>
+    <pattern id="pattern1" gifindex="6" width="10" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <circle cx="4.5" cy="3.5" r="0.5" height="1" />
+      <rect x="9" y="1" width="1" height="1" />
+    </pattern>
+    <pattern id="pattern2" gifindex="2" width="4" height="4" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+    </pattern>
+    <pattern id="pattern3" gifindex="3" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+    </pattern>
+    <pattern id="pattern4" gifindex="4" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+      <rect x="0" y="1" width="4" height="1"/>
+    </pattern>
+    <pattern id="pattern5" gifindex="5" width="4" height="4" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+      <rect x="0" y="1" width="4" height="1"/>
+    </pattern>
+    <pattern id="pattern6" gifindex="12" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <circle cx="3.5" cy="1.5" r="0.5" height="1" />
+      <circle cx="1.5" cy="3.5" r="0.5" height="1" />
+    </pattern>
+    <pattern id="pattern7" gifindex="7" width="20" height="20" patternTransform="scale(0.5)" patternUnits="userSpaceOnUse" fill="black">
+      <path d="M8,0 h2 v1 h-2 z"/>
+      <path d="M18,0 h2 v11 h-2 v-5 h-5 v-2 h5 z"/>
+      <path d="M0,4 h5 v2 h-5 "/>
+      <path d="M8,9 h2 v5 h5 v2 h-5 v4 h-2 v-4 h-5 v-2 h5 z"/>
+    </pattern>
+    <pattern id="pattern8" gifindex="8" width="4" height="4" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+    </pattern>
+    <pattern id="pattern9" gifindex="9" width="4" height="4" patternTransform="rotate(90 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="4"/>
+    </pattern>
+    <pattern id="pattern10" gifindex="10" width="6" height="6" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="1" width="6" height="1"/>
+      <rect x="1" y="0" width="1" height="6"/>
+    </pattern>
+    <pattern id="pattern11" gifindex="11" width="6" height="6" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="1" width="6" height="1"/>
+      <rect x="1" y="0" width="1" height="6"/>
+    </pattern>
+    <pattern id="pattern12" gifindex="1" width="8" height="8" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="1" height="4"/>
+      <rect x="5" y="0" width="3" height="4"/>
+      <rect x="1" y="4" width="4" height="4"/>
+    </pattern>
+    <pattern id="pattern13" gifindex="13" width="2" height="2" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1" height="1" />
+      <rect x="0" y="1" width="1" height="1" />
+    </pattern>
+    <pattern id="pattern14" gifindex="14" width="4" height="4" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="3" height="4"/>
+    </pattern>
+    <pattern id="pattern15" gifindex="21" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="3" height="4"/>
+    </pattern>
+    <pattern id="pattern16" gifindex="16" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="1" height="3" />
+      <rect x="2" y="0" width="2" height="3" />
+    </pattern>
+    <pattern id="pattern17" gifindex="17" width="4" height="4" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1.5" height="4"/>
+    </pattern>
+    <pattern id="pattern18" gifindex="18" width="10" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <path d="M0,0 h5 v1 h-1 v1 h1 v2 h-5 z"/>
+      <path d="M4,0 h6 v3 h-1 v1 h-5 v-2 h1 v-1 h-1 z"/>
+    </pattern>
+    <pattern id="pattern19" gifindex="19" width="20" height="20" patternTransform="scale(0.5)" patternUnits="userSpaceOnUse" fill="black">
+      <path d="M0,0 h8 v1 h2 v-1 h8 v4 h-5 v2 h5 v5 h2 v9 h-10 v-4 h5 v-2 h-5 v-5 h-2 v5 h-5 v2 h5 v4 h-8 v-14 h5 v-2 h-5 z"/>
+    </pattern>
+    <pattern id="pattern20" gifindex="20" width="4" height="4" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="3" height="4"/>
+    </pattern>
+    <pattern id="pattern21" gifindex="27" width="1" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="1" height="1"/>
+      <rect x="0" y="2" width="1" height="2"/>
+    </pattern>
+    <pattern id="pattern22" gifindex="22" width="5" height="5" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="1" width="4" height="4" />
+    </pattern>
+    <pattern id="pattern23" gifindex="23" width="4" height="4" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="1.5" height="4"/>
+    </pattern>
+    <pattern id="pattern24" gifindex="24" width="4" height="4" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="4" height="1" />
+      <rect x="0" y="1" width="1" height="1" />
+      <rect x="2" y="1" width="2" height="1" />
+      <rect x="0" y="2" width="4" height="1" />
+      <rect x="0" y="3" width="3" height="1" />
+    </pattern>
+    <pattern id="pattern25" gifindex="25" width="4" height="4" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="2" height="4"/>
+    </pattern>
+    <pattern id="pattern26" gifindex="26" width="4" height="4" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="0" width="2" height="4"/>
+    </pattern>
+    <pattern id="pattern27" gifindex="15" width="2" height="1" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="1" height="1" />
+    </pattern>
+    <pattern id="pattern28" gifindex="28" width="8" height="8" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="0" y="0" width="8" height="1"/>
+      <rect x="0" y="1" width="1" height="7"/>
+      <rect x="5" y="0" width="3" height="8"/>
+      <rect x="1" y="5" width="4" height="3"/>
+    </pattern>
+    <pattern id="pattern29" gifindex="29" width="5" height="5" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" fill="black">
+      <rect x="1" y="1" width="4" height="4" />
+    </pattern>
+  </defs>
+
+  <!-- A palette of pattern samples. -->
+  <rect x="0" y="0" width="20" height="20" fill="url(#pattern0)" />
+  <rect x="20" y="0" width="20" height="20" fill="url(#pattern1)" />
+  <rect x="40" y="0" width="20" height="20" fill="url(#pattern2)" />
+  <rect x="60" y="0" width="20" height="20" fill="url(#pattern3)" />
+  <rect x="80" y="0" width="20" height="20" fill="url(#pattern4)" />
+  <rect x="100" y="0" width="20" height="20" fill="url(#pattern5)" />
+  <rect x="0" y="20" width="20" height="20" fill="url(#pattern6)" />
+  <rect x="20" y="20" width="20" height="20" fill="url(#pattern7)" />
+  <rect x="40" y="20" width="20" height="20" fill="url(#pattern8)" />
+  <rect x="60" y="20" width="20" height="20" fill="url(#pattern9)" />
+  <rect x="80" y="20" width="20" height="20" fill="url(#pattern10)" />
+  <rect x="100" y="20" width="20" height="20" fill="url(#pattern11)" />
+  <rect x="0" y="40" width="20" height="20" fill="url(#pattern12)" />
+  <rect x="20" y="40" width="20" height="20" fill="url(#pattern13)" />
+  <rect x="40" y="40" width="20" height="20" fill="url(#pattern14)" />
+  <rect x="60" y="40" width="20" height="20" fill="url(#pattern15)" />
+  <rect x="80" y="40" width="20" height="20" fill="url(#pattern16)" />
+  <rect x="100" y="40" width="20" height="20" fill="url(#pattern17)" />
+  <rect x="0" y="60" width="20" height="20" fill="url(#pattern18)" />
+  <rect x="20" y="60" width="20" height="20" fill="url(#pattern19)" />
+  <rect x="40" y="60" width="20" height="20" fill="url(#pattern20)" />
+  <rect x="60" y="60" width="20" height="20" fill="url(#pattern21)" />
+  <rect x="80" y="60" width="20" height="20" fill="url(#pattern22)" />
+  <rect x="100" y="60" width="20" height="20" fill="url(#pattern23)" />
+  <rect x="0" y="80" width="20" height="20" fill="url(#pattern24)" />
+  <rect x="20" y="80" width="20" height="20" fill="url(#pattern25)" />
+  <rect x="40" y="80" width="20" height="20" fill="url(#pattern26)" />
+  <rect x="60" y="80" width="20" height="20" fill="url(#pattern27)" />
+  <rect x="80" y="80" width="20" height="20" fill="url(#pattern28)" />
+  <rect x="100" y="80" width="20" height="20" fill="url(#pattern29)" />
+</svg>
diff --git a/rust/pspp/src/spv/read/graph/patterns/.gitignore b/rust/pspp/src/spv/read/graph/patterns/.gitignore
new file mode 100644 (file)
index 0000000..3100bf1
--- /dev/null
@@ -0,0 +1 @@
+data.rs
diff --git a/rust/pspp/src/spv/read/graph/style.rs b/rust/pspp/src/spv/read/graph/style.rs
new file mode 100644 (file)
index 0000000..e9cb639
--- /dev/null
@@ -0,0 +1,331 @@
+use serde::{Deserialize, Deserializer};
+
+use crate::{
+    output::pivot::{
+        Length,
+        look::{Color, HorzAlign, VertAlign},
+    },
+    spv::read::graph::{
+        LocationLength, LocationLengthRange,
+        patterns::{ColoredPattern, Patterns},
+    },
+};
+
+#[derive(Clone, Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct StyleCycle {
+    #[serde(rename = "@id")]
+    pub id: Option<String>,
+
+    #[serde(default, rename = "cycle")]
+    cycles: Vec<Cycle>,
+}
+
+impl StyleCycle {
+    #[allow(dead_code)]
+    pub fn get_style(&self, index: usize) -> Style {
+        let mut iter = self.get_style_components(index);
+        let style = iter.next().cloned().unwrap_or_default();
+        iter.fold(style, |a, b| a.overridden_with(b))
+    }
+
+    /// Iterates through the styles for the given 1-based `index` in this cycle.
+    ///
+    /// There might be no styles, if `self.cycles` is empty or all of the
+    /// elements in `self.cycles` are empty.
+    pub fn get_style_components(&self, index: usize) -> impl Iterator<Item = &Style> {
+        // Treat index 0 as the last style in the cycle.
+        let mut index = index.checked_sub(1).unwrap_or_else(|| self.len() - 1);
+        self.cycles
+            .iter()
+            .flat_map(move |cycle| match cycle.styles.len() {
+                0 => None,
+                len => {
+                    let s = &cycle.styles[index % len];
+                    index /= len;
+                    Some(s)
+                }
+            })
+    }
+
+    /// Returns the length of the cycle, that is, the number of styles before
+    /// the cycle repeats.
+    pub fn len(&self) -> usize {
+        self.cycles
+            .iter()
+            .map(|cycle| cycle.styles.len().max(1))
+            .product()
+    }
+}
+
+#[derive(Clone, Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct Cycle {
+    #[serde(default, rename = "style")]
+    styles: Vec<Style>,
+}
+
+#[derive(Clone, Deserialize, Debug, Default)]
+#[serde(rename_all = "camelCase")]
+#[serde(default)]
+pub struct Style {
+    #[serde(rename = "@id")]
+    pub id: Option<String>,
+
+    /// The text color or, in some cases, background color.
+    #[serde(rename = "@color")]
+    pub color: Option<Color>,
+
+    /// The opacity of `color`, in the range 0 to 1, overriding the color's
+    /// alpha channel.
+    #[serde(rename = "@opacity")]
+    pub opacity: Option<f64>,
+
+    #[serde(rename = "@color2")]
+    pub color2: Option<Color>,
+
+    /// The opacity of `color2`, in the range 0 to 1, overriding the color's
+    /// alpha channel.
+    #[serde(rename = "@opacity2")]
+    pub opacity2: Option<f64>,
+
+    #[serde(rename = "@visible")]
+    pub visible: Option<bool>,
+
+    #[serde(rename = "@padding")]
+    pub padding: Option<Length>,
+
+    #[serde(rename = "@pattern")]
+    pub pattern: Option<usize>,
+
+    #[serde(rename = "@symbol")]
+    pub symbol: Option<String>,
+
+    #[serde(rename = "@glyph-sides")]
+    pub symbol_sides: Option<usize>,
+
+    #[serde(rename = "@glyph-angle")]
+    pub symbol_angle: Option<f64>,
+
+    #[serde(rename = "@size")]
+    pub symbol_size: Option<LocationLength>,
+
+    #[serde(rename = "@glyph-aspect")]
+    pub symbol_aspect: Option<f64>,
+
+    #[serde(rename = "@stroke-dasharray")]
+    stroke_dasharray: Option<String>,
+
+    #[serde(rename = "@stroke-linecap")]
+    stroke_linecap: Option<StrokeLinecap>,
+
+    #[serde(rename = "@stroke-width")]
+    stroke_width: Option<Length>,
+
+    #[serde(rename = "@stroke-decoration")]
+    stroke_decoration: Option<StrokeDecoration>,
+
+    #[serde(rename = "@font-family")]
+    font_family: Option<String>,
+
+    #[serde(rename = "@font-size")]
+    font_size: Option<LocationLengthRange>,
+
+    #[serde(rename = "@font-weight")]
+    weight: Option<FontWeight>,
+
+    #[serde(rename = "@font-style")]
+    font_slant: Option<FontSlant>,
+
+    #[serde(rename = "@font-underline")]
+    font_underline: Option<FontUnderline>,
+
+    #[serde(rename = "@textAlignment")]
+    text_alignment: Option<TextAlignment>,
+
+    #[serde(rename = "@decimal-offset")]
+    decimal_offset: Option<Length>,
+
+    #[serde(rename = "@labelLocationVertical")]
+    #[serde(deserialize_with = "deserialize_vert_align")]
+    label_vert_align: Option<VertAlign>,
+
+    #[serde(rename = "@ignoreNewLines")]
+    ignore_new_lines: Option<bool>,
+
+    #[serde(rename = "@labelInside")]
+    label_placement: Option<LabelPlacement>,
+
+    #[serde(default, rename = "$value")]
+    pub children: Vec<Style>,
+
+    #[serde(rename = "@text-fit")]
+    text_fit: Option<bool>,
+}
+
+impl Style {
+    /// Updates this style with the overrides in `override`.
+    pub fn overridden_with(self, overrides: &Style) -> Self {
+        Self {
+            id: overrides.id.clone().or(self.id),
+            color: overrides.color.or(self.color),
+            opacity: overrides.opacity.or(self.opacity),
+            color2: overrides.color2.or(self.color2),
+            opacity2: overrides.opacity2.or(self.opacity2),
+            visible: overrides.visible.or(self.visible),
+            padding: overrides.padding.or(self.padding),
+            pattern: overrides.pattern.or(self.pattern),
+            symbol: overrides.symbol.clone().or(self.symbol),
+            symbol_sides: overrides.symbol_sides.or(self.symbol_sides),
+            symbol_angle: overrides.symbol_angle.or(self.symbol_angle),
+            symbol_size: overrides.symbol_size.clone().or(self.symbol_size),
+            symbol_aspect: overrides.symbol_aspect.or(self.symbol_aspect),
+            stroke_dasharray: overrides.stroke_dasharray.clone().or(self.stroke_dasharray),
+            stroke_linecap: overrides.stroke_linecap.or(self.stroke_linecap),
+            stroke_width: overrides.stroke_width.or(self.stroke_width),
+            stroke_decoration: overrides.stroke_decoration.or(self.stroke_decoration),
+            font_family: overrides.font_family.clone().or(self.font_family),
+            font_size: overrides.font_size.clone().or(self.font_size),
+            weight: overrides.weight.or(self.weight),
+            font_slant: overrides.font_slant.or(self.font_slant),
+            font_underline: overrides.font_underline.or(self.font_underline),
+            text_alignment: overrides.text_alignment.or(self.text_alignment),
+            label_vert_align: overrides.label_vert_align.or(self.label_vert_align),
+            decimal_offset: overrides.decimal_offset.or(self.decimal_offset),
+            ignore_new_lines: overrides.ignore_new_lines.or(self.ignore_new_lines),
+            label_placement: overrides.label_placement.or(self.label_placement),
+            text_fit: overrides.text_fit.or(self.text_fit),
+            children: self.children,
+        }
+    }
+
+    pub fn label_horz_align(&self) -> Option<HorzAlign> {
+        match self.text_alignment.unwrap_or(TextAlignment::Center) {
+            TextAlignment::Left => Some(HorzAlign::Left),
+            TextAlignment::Right => Some(HorzAlign::Right),
+            TextAlignment::Center => Some(HorzAlign::Center),
+            TextAlignment::Decimal => match self.decimal_offset {
+                Some(offset) => Some(HorzAlign::Decimal {
+                    offset: offset.as_px_f64(),
+                }),
+                None => None,
+            },
+            TextAlignment::Mixed => None,
+        }
+    }
+
+    pub fn fill(&self, patterns: &mut Patterns) -> String {
+        patterns.insert(ColoredPattern {
+            index: self.pattern.unwrap_or_default(),
+            color: self.color(Color::BLACK),
+            color2: self.color2(Color::WHITE),
+        })
+    }
+
+    fn color_with_opacity(color: Color, opacity: Option<f64>) -> Color {
+        if let Some(opacity) = opacity
+            && (0.0..=1.0).contains(&opacity)
+        {
+            color.with_alpha((opacity * 255.0) as u8)
+        } else {
+            color
+        }
+    }
+
+    pub fn color(&self, default: Color) -> Color {
+        Self::color_with_opacity(self.color.unwrap_or(default), self.opacity)
+    }
+
+    pub fn color2(&self, default: Color) -> Color {
+        Self::color_with_opacity(self.color2.unwrap_or(default), self.opacity2)
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum LabelPlacement {
+    /// Place label inside target.
+    #[serde(rename = "true")]
+    Inside,
+
+    /// Place label outside target.
+    #[serde(rename = "false")]
+    Outside,
+
+    /// Label inside if it fits, otherwise outside.
+    #[serde(rename = "preferred")]
+    PreferInside,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum FontWeight {
+    Regular,
+    Bold,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum FontSlant {
+    Regular,
+    Italic,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum FontUnderline {
+    None,
+    Underline,
+}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum TextAlignment {
+    Left,
+    Right,
+    Center,
+    Decimal,
+    #[default]
+    Mixed,
+}
+
+fn deserialize_vert_align<'de, D>(deserializer: D) -> Result<Option<VertAlign>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+    #[serde(rename_all = "camelCase")]
+    enum LabelLocationVertical {
+        Positive,
+        Negative,
+        Center,
+    }
+
+    impl From<LabelLocationVertical> for VertAlign {
+        fn from(value: LabelLocationVertical) -> Self {
+            match value {
+                LabelLocationVertical::Positive => VertAlign::Top,
+                LabelLocationVertical::Negative => VertAlign::Bottom,
+                LabelLocationVertical::Center => VertAlign::Middle,
+            }
+        }
+    }
+
+    Ok(<Option<LabelLocationVertical>>::deserialize(deserializer)?.map(VertAlign::from))
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum StrokeLinecap {
+    Round,
+    Square,
+    Butt,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum StrokeDecoration {
+    EtchLowered,
+    EtchRaised,
+    Bevel,
+}
diff --git a/rust/pspp/src/spv/read/graph/symbols.rs b/rust/pspp/src/spv/read/graph/symbols.rs
new file mode 100644 (file)
index 0000000..d6025ef
--- /dev/null
@@ -0,0 +1,223 @@
+#![allow(dead_code)]
+use std::f64::consts::PI;
+use std::fmt::Write as _;
+
+use svg::{
+    Node,
+    node::{
+        Blob,
+        element::{Group, Line, Polygon},
+    },
+};
+
+use crate::spv::read::graph::symbols::data::SYMBOLS;
+
+mod data;
+
+#[derive(Copy, Clone, Debug)]
+pub enum SymbolKind {
+    Fixed(
+        /// Definition as SVG as textual XML.
+        &'static str,
+    ),
+    Polygon(
+        /// Number of sides on the polygon (at least 3).
+        usize,
+    ),
+    Star(
+        /// Number of points on the star (at least 3).
+        usize,
+    ),
+    Flower(
+        /// Number of petals on the flower (at least 3).
+        usize,
+    ),
+}
+
+impl SymbolKind {
+    pub fn with_sides(self, sides: usize) -> Self {
+        match self {
+            SymbolKind::Fixed(_) => self,
+            SymbolKind::Polygon(_) => Self::Polygon(sides),
+            SymbolKind::Star(_) => Self::Star(sides),
+            SymbolKind::Flower(_) => Self::Flower(sides),
+        }
+    }
+
+    pub fn to_svg(&self) -> Group {
+        fn round(f: f64) -> f64 {
+            (f * 100_000.0).round() / 100_000.0
+        }
+
+        match *self {
+            SymbolKind::Fixed(xml) => Group::new().add(Blob::new(xml)),
+            SymbolKind::Polygon(n) => {
+                let n = n.clamp(3, 100);
+                let mut points = String::new();
+                for i in 0..=n {
+                    let d = (i * 2) as f64 * PI / n as f64;
+                    write!(
+                        &mut points,
+                        "{},{} ",
+                        round(5.0 - 5.0 * d.sin()),
+                        round(5.0 - 5.0 * d.cos()),
+                    )
+                    .unwrap();
+                }
+                points.pop();
+                Group::new().add(Polygon::new().set("points", points))
+            }
+            SymbolKind::Star(n) => {
+                let n = n.clamp(3, 100);
+                let mut points = String::new();
+                for i in 1..=n {
+                    let d = (i * 2 - 1) as f64 * PI / n as f64;
+                    let d2 = (i * 2) as f64 * PI / n as f64;
+                    write!(
+                        &mut points,
+                        "{},{} {},{} ",
+                        round(5.0 + 2.0 * d.sin()),
+                        round(5.0 - 2.0 * d.cos()),
+                        round(5.0 + 5.0 * d2.sin()),
+                        round(5.0 - 5.0 * d2.cos())
+                    )
+                    .unwrap();
+                }
+                points.pop();
+                Group::new().add(Polygon::new().set("points", points))
+            }
+            SymbolKind::Flower(n) => {
+                let n = n.clamp(3, 100);
+                let mut group = Group::new();
+                for i in 0..n {
+                    let d = PI * 2.0 * i as f64 / n as f64;
+                    group.append(
+                        Line::new()
+                            .set("x1", 5)
+                            .set("y1", 5)
+                            .set("x2", round(5.0 + 5.0 * d.sin()))
+                            .set("y2", round(5.0 - 5.0 * d.cos())),
+                    );
+                }
+                group
+            }
+        }
+    }
+}
+
+/// A symbol for marking points in a graph.
+#[derive(Copy, Clone, Debug)]
+pub struct Symbol {
+    /// The symbol's name.
+    pub name: &'static str,
+
+    /// Whether the symbol is hollow:
+    ///
+    /// - A hollow symbol has a stroke color.  It should not have a fill color.
+    /// - A non-hollow symbol has a stroke color and an optional fill color.
+    pub hollow: bool,
+
+    /// Kind of symbol.
+    pub kind: SymbolKind,
+}
+
+impl Symbol {
+    /// Returns the symbol with the given name, or `None` if there is none.
+    ///
+    /// Symbol names are matched case-insensitively.  Use the `name` member of
+    /// the returned symbol for the canonical name.
+    ///
+    /// Some symbols have a parameterized number of sides.  These are returned
+    /// with a default number of sides.  Use [with_sides](Self::with_sides) to
+    /// change the number of sides.
+    pub fn for_name(name: &str) -> Option<Self> {
+        SYMBOLS
+            .iter()
+            .find(|symbol| symbol.name.eq_ignore_ascii_case(name))
+            .copied()
+    }
+
+    /// Returns true if the symbol is hollow.
+    pub fn is_hollow(&self) -> bool {
+        self.hollow
+    }
+
+    pub fn with_sides(self, sides: usize) -> Self {
+        Self {
+            kind: self.kind.with_sides(sides),
+            ..self
+        }
+    }
+
+    pub fn to_svg(&self) -> Group {
+        self.kind.to_svg()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::spv::read::graph::symbols::Symbol;
+
+    #[test]
+    fn flower() {
+        let symbol = Symbol::for_name("flower").unwrap().with_sides(6);
+        assert_eq!(
+            symbol.to_svg().to_string(),
+            r#"<g>
+<line x1="5" x2="5" y1="5" y2="0"/>
+<line x1="5" x2="9.33013" y1="5" y2="2.5"/>
+<line x1="5" x2="9.33013" y1="5" y2="7.5"/>
+<line x1="5" x2="5" y1="5" y2="10"/>
+<line x1="5" x2="0.66987" y1="5" y2="7.5"/>
+<line x1="5" x2="0.66987" y1="5" y2="2.5"/>
+</g>"#
+        );
+    }
+
+    #[test]
+    fn star() {
+        let symbol = Symbol::for_name("star").unwrap().with_sides(6);
+        assert_eq!(
+            symbol.to_svg().to_string(),
+            r#"<g>
+<polygon points="6,3.26795 9.33013,2.5 7,5 9.33013,7.5 6,6.73205 5,10 4,6.73205 0.66987,7.5 3,5 0.66987,2.5 4,3.26795 5,0"/>
+</g>"#
+        );
+    }
+
+    #[test]
+    fn polygon() {
+        let symbol = Symbol::for_name("polygon").unwrap().with_sides(6);
+        assert_eq!(
+            symbol.to_svg().to_string(),
+            r#"<g>
+<polygon points="5,0 0.66987,2.5 0.66987,7.5 5,10 9.33013,7.5 9.33013,2.5 5,0"/>
+</g>"#
+        );
+    }
+
+    #[test]
+    fn square() {
+        let symbol = Symbol::for_name("SQUARE").unwrap();
+        assert_eq!(symbol.name, "square");
+        assert_eq!(symbol.hollow, false);
+        assert_eq!(
+            symbol.to_svg().to_string(),
+            r#"<g>
+      <rect x="0" y="0" width="10" height="10" />
+    
+</g>"#
+        );
+
+        let symbol = Symbol::for_name("hollowsquare").unwrap();
+        assert_eq!(symbol.name, "hollowSquare");
+        assert_eq!(symbol.hollow, true);
+        assert_eq!(
+            symbol.to_svg().to_string(),
+            r#"<g>
+      <rect x="0" y="0" width="10" height="10" />
+    
+</g>"#
+        );
+    }
+}
diff --git a/rust/pspp/src/spv/read/graph/symbols.svg b/rust/pspp/src/spv/read/graph/symbols.svg
new file mode 100644 (file)
index 0000000..456152d
--- /dev/null
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<svg xmlns="http://www.w3.org/2000/svg">
+  <defs>
+    <!--
+        An SVG <pattern> for each of the symbols supported by VizML.
+
+        Each pattern nominally occupies the box (0,0)-(10,10), although a few
+        spill over the margin.
+    -->
+    <g id="none" hollow="true" />
+    <g id="square" hollow="false">
+      <rect x="0" y="0" width="10" height="10" />
+    </g>
+    <g id="arc" hollow="true">
+      <path d="M0,5 A5 5 180 1 1 10,5" fill="none" />
+    </g>
+    <g id="arcDown" hollow="true">
+      <path d="M0,5 A5 5 180 1 0 10,5" fill="none" />
+    </g>
+    <g id="circle" hollow="false">
+      <circle cx="5" cy="5" r="5" />
+    </g>
+    <g id="cross" hollow="true">
+      <line x1="0" y1="0" x2="10" y2="10" />
+      <line x1="0" y1="10" x2="10" y2="0" />
+    </g>
+    <g id="plus" hollow="true">
+      <line x1="0" y1="5" x2="10" y2="5" />
+      <line x1="5" y1="0" x2="5" y2="10" />
+    </g>
+    <g id="arrow" hollow="true">
+      <!-- points up -->
+      <line x1="5" y1="0" x2="5" y2="10" />
+      <line x1="5" y1="0" x2="2.5" y2="2.5" />
+      <line x1="5" y1="0" x2="7.5" y2="2.5" />
+    </g>
+    <g id="line" hollow="true">
+      <line x1="0" y1="5" x2="10" y2="5" />
+    </g>
+    <g id="male" hollow="false">
+      <circle cx="5" cy="5" r="3.33333" />
+      <line x1="7.35702" y1="2.64298" x2="9.71404" y2="0.28596" />
+      <line x1="9.71404" y1="0.28596" x2="7.35702" y2="0.28596" />
+      <line x1="9.71404" y1="0.28596" x2="9.71404" y2="2.64298" />
+    </g>
+    <g id="female" hollow="false">
+      <circle cx="5" cy="5" r="3.33333" />
+      <line x1="5" y1="8.33333" x2="5" y2="11.66666" />
+      <line x1="3.33333" y1="10" x2="6.66667" y2="10" />
+    </g>
+    <g id="bowtie" hollow="false">
+      <path d="M5,5 L10,7.5 V2.5 Z" />
+      <path d="M5,5 L0,7.5 V2.5 Z" />
+    </g>
+    <g id="ibeam" hollow="true">
+      <line x1="2.5" y1="0" x2="7.5" y2="0" />
+      <line x1="5" y1="0" x2="5" y2="10" />
+      <line x1="2.5" y1="10" x2="7.5" y2="10" />
+    </g>
+    <g id="roundRectangle" hollow="false">
+      <rect x="0" y="0" width="10" height="10" rx="2.5" ry="2.5" />
+    </g>
+    <!--
+        The contents for these flowers are ignored; PSPP regenerates them
+        internally.  They are supplied only for the names and to show as
+        examples in the palette.
+    -->
+    <g id="flower" hollow="true" kind="flower" sides="5"/>
+    <g id="flower3" hollow="true" kind="flower" sides="3">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="9.330127018922195" y2="7.499999999999999" />
+      <line x1="5" y1="5" x2="0.6698729810778081" y2="7.500000000000002" />
+    </g>
+    <g id="flower4" hollow="true" kind="flower" sides="4">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="10.0" y2="5.0" />
+      <line x1="5" y1="5" x2="5.000000000000001" y2="10.0" />
+      <line x1="5" y1="5" x2="0.0" y2="5.000000000000001" />
+    </g>
+    <g id="flower5" hollow="true" kind="flower" sides="5">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="9.755282581475768" y2="3.4549150281252627" />
+      <line x1="5" y1="5" x2="7.938926261462367" y2="9.045084971874736" />
+      <line x1="5" y1="5" x2="2.061073738537635" y2="9.045084971874738" />
+      <line x1="5" y1="5" x2="0.2447174185242318" y2="3.4549150281252636" />
+    </g>
+    <g id="flower6" hollow="true" kind="flower" sides="6">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="9.330127018922193" y2="2.4999999999999996" />
+      <line x1="5" y1="5" x2="9.330127018922195" y2="7.499999999999999" />
+      <line x1="5" y1="5" x2="5.000000000000001" y2="10.0" />
+      <line x1="5" y1="5" x2="0.6698729810778081" y2="7.500000000000002" />
+      <line x1="5" y1="5" x2="0.6698729810778072" y2="2.4999999999999996" />
+    </g>
+    <g id="flower7" hollow="true" kind="flower" sides="7">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="8.90915741234015" y2="1.8825509907063323" />
+      <line x1="5" y1="5" x2="9.874639560909118" y2="6.112604669781572" />
+      <line x1="5" y1="5" x2="7.169418695587791" y2="9.504844339512095" />
+      <line x1="5" y1="5" x2="2.83058130441221" y2="9.504844339512097" />
+      <line x1="5" y1="5" x2="0.12536043909088157" y2="6.1126046697815735" />
+      <line x1="5" y1="5" x2="1.0908425876598504" y2="1.8825509907063331" />
+    </g>
+    <g id="flower8" hollow="true" kind="flower" sides="8">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="8.535533905932738" y2="1.4644660940672622" />
+      <line x1="5" y1="5" x2="10.0" y2="5.0" />
+      <line x1="5" y1="5" x2="8.535533905932738" y2="8.535533905932738" />
+      <line x1="5" y1="5" x2="5.000000000000001" y2="10.0" />
+      <line x1="5" y1="5" x2="1.4644660940672627" y2="8.535533905932738" />
+      <line x1="5" y1="5" x2="0.0" y2="5.000000000000001" />
+      <line x1="5" y1="5" x2="1.4644660940672614" y2="1.4644660940672631" />
+    </g>
+    <g id="flower9" hollow="true" kind="flower" sides="9">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="8.213938048432697" y2="1.16977778440511" />
+      <line x1="5" y1="5" x2="9.92403876506104" y2="4.131759111665348" />
+      <line x1="5" y1="5" x2="9.330127018922195" y2="7.499999999999999" />
+      <line x1="5" y1="5" x2="6.710100716628345" y2="9.69846310392954" />
+      <line x1="5" y1="5" x2="3.289899283371657" y2="9.698463103929543" />
+      <line x1="5" y1="5" x2="0.6698729810778081" y2="7.500000000000002" />
+      <line x1="5" y1="5" x2="0.07596123493895934" y2="4.1317591116653505" />
+      <line x1="5" y1="5" x2="1.786061951567302" y2="1.169777784405111" />
+    </g>
+    <g id="flower10" hollow="true" kind="flower" sides="10">
+      <line x1="5" y1="5" x2="5.0" y2="0.0" />
+      <line x1="5" y1="5" x2="7.938926261462366" y2="0.9549150281252627" />
+      <line x1="5" y1="5" x2="9.755282581475768" y2="3.4549150281252627" />
+      <line x1="5" y1="5" x2="9.755282581475768" y2="6.545084971874736" />
+      <line x1="5" y1="5" x2="7.938926261462367" y2="9.045084971874736" />
+      <line x1="5" y1="5" x2="5.000000000000001" y2="10.0" />
+      <line x1="5" y1="5" x2="2.061073738537635" y2="9.045084971874738" />
+      <line x1="5" y1="5" x2="0.24471741852423268" y2="6.545084971874738" />
+      <line x1="5" y1="5" x2="0.2447174185242318" y2="3.4549150281252636" />
+      <line x1="5" y1="5" x2="2.061073738537633" y2="0.9549150281252636" />
+    </g>
+    <!--
+        The contents for these polygons are ignored; PSPP regenerates them
+        internally.  They are supplied only for the names and to show as
+        examples in the palette.
+    -->
+    <g id="polygon" hollow="false" kind="polygon" sides="5"/>
+    <g id="triangle" hollow="false" kind="polygon" sides="3">
+      <polygon points="5.0 0.0 0.6698729810778064 7.499999999999999 9.330127018922191 7.500000000000002 5.000000000000001 0.0" />
+    </g>
+    <g id="diamond" hollow="false" kind="polygon" sides="4">
+      <polygon points="5,0 0,5 4.999999999999999,10 10,5.000000000000001 5.000000000000001,0"/>
+    </g>
+    <g id="pentagon" hollow="false" kind="polygon" sides="5">
+      <polygon points="5.0 0.0 0.24471741852423268 3.4549150281252627 2.0610737385376336 9.045084971874736 7.938926261462365 9.045084971874738 9.755282581475768 3.4549150281252636 5.000000000000001 0.0" />
+    </g>
+    <g id="hexagon" hollow="false" kind="polygon" sides="6">
+      <polygon points="5.0 0.0 0.6698729810778072 2.4999999999999996 0.6698729810778064 7.499999999999999 4.999999999999999 10.0 9.330127018922191 7.500000000000002 9.330127018922193 2.4999999999999996 5.000000000000001 0.0" />
+    </g>
+    <g id="heptagon" hollow="false" kind="polygon" sides="7">
+      <polygon points="5.0 0.0 1.0908425876598509 1.8825509907063323 0.12536043909088157 6.112604669781572 2.830581304412209 9.504844339512095 7.16941869558779 9.504844339512097 9.874639560909118 6.1126046697815735 8.90915741234015 1.8825509907063331 5.000000000000001 0.0" />
+    </g>
+    <g id="octagon" hollow="false" kind="polygon" sides="8">
+      <polygon points="5.0 0.0 1.4644660940672627 1.4644660940672622 0.0 5.0 1.4644660940672622 8.535533905932738 4.999999999999999 10.0 8.535533905932738 8.535533905932738 10.0 5.000000000000001 8.535533905932738 1.4644660940672631 5.000000000000001 0.0" />
+    </g>
+    <g id="nonagon" hollow="false" kind="polygon" sides="9">
+      <polygon points="5.0 0.0 1.7860619515673037 1.16977778440511 0.07596123493896023 4.131759111665348 0.6698729810778064 7.499999999999999 3.2898992833716556 9.69846310392954 6.710100716628343 9.698463103929543 9.330127018922191 7.500000000000002 9.924038765061042 4.1317591116653505 8.213938048432698 1.169777784405111 5.000000000000001 0.0" />
+    </g>
+    <g id="decagon" hollow="false" kind="polygon" sides="10">
+      <polygon points="5.0 0.0 2.061073738537634 0.9549150281252627 0.24471741852423268 3.4549150281252627 0.2447174185242318 6.545084971874736 2.0610737385376336 9.045084971874736 4.999999999999999 10.0 7.938926261462365 9.045084971874738 9.755282581475768 6.545084971874738 9.755282581475768 3.4549150281252636 7.938926261462367 0.9549150281252636 5.000000000000001 0.0" />
+    </g>
+    <!--
+        The contents for these stars are ignored; PSPP regenerates them
+        internally.  They are supplied only for the names and to show as
+        examples in the palette.
+    -->
+    <g id="star" hollow="false" kind="star" sides="5"/>
+    <g id="star3" hollow="false" kind="star" sides="3">
+      <polygon points="6.732050807568877 4.0 9.330127018922195 7.499999999999999 5.0 7.0 0.6698729810778081 7.500000000000002 3.267949192431123 4.0 4.999999999999999 0.0" />
+    </g>
+    <g id="star4" hollow="false" kind="star" sides="4">
+      <polygon points="6.414213562373095 3.585786437626905 10.0 5.0 6.414213562373095 6.414213562373095 5.000000000000001 10.0 3.585786437626905 6.414213562373096 0.0 5.000000000000001 3.5857864376269046 3.585786437626905 4.999999999999999 0.0" />
+    </g>
+    <g id="star5" hollow="false" kind="star" sides="5">
+      <polygon points="6.175570504584947 3.381966011250105 9.755282581475768 3.4549150281252627 6.902113032590307 5.618033988749895 7.938926261462367 9.045084971874736 5.0 7.0 2.061073738537635 9.045084971874738 3.0978869674096927 5.618033988749895 0.2447174185242318 3.4549150281252636 3.8244294954150533 3.381966011250105 4.999999999999999 0.0" />
+    </g>
+    <g id="star6" hollow="false" kind="star" sides="6">
+      <polygon points="6.0 3.2679491924311224 9.330127018922193 2.4999999999999996 7.0 5.0 9.330127018922195 7.499999999999999 6.0 6.732050807568878 5.000000000000001 10.0 4.000000000000001 6.732050807568878 0.6698729810778081 7.500000000000002 3.0 5.0 0.6698729810778072 2.4999999999999996 3.999999999999999 3.2679491924311233 4.999999999999999 0.0" />
+    </g>
+    <g id="star7" hollow="false" kind="star" sides="7">
+      <polygon points="5.867767478235116 3.198062264195162 8.90915741234015 1.8825509907063323 6.949855824363647 4.554958132087371 9.874639560909118 6.112604669781572 6.56366296493606 6.246979603717467 7.169418695587791 9.504844339512095 5.0 7.0 2.83058130441221 9.504844339512097 3.4363370350639406 6.246979603717468 0.12536043909088157 6.1126046697815735 3.050144175636352 4.554958132087373 1.0908425876598504 1.8825509907063331 4.132232521764885 3.198062264195161 4.999999999999999 0.0" />
+    </g>
+    <g id="star8" hollow="false" kind="star" sides="8">
+      <polygon points="5.765366864730179 3.1522409349774265 8.535533905932738 1.4644660940672622 6.847759065022574 4.234633135269821 10.0 5.0 6.847759065022574 5.765366864730179 8.535533905932738 8.535533905932738 5.76536686473018 6.847759065022574 5.000000000000001 10.0 4.234633135269821 6.847759065022574 1.4644660940672627 8.535533905932738 3.152240934977427 5.765366864730181 0.0 5.000000000000001 3.152240934977427 4.23463313526982 1.4644660940672614 1.4644660940672631 4.234633135269819 3.152240934977427 4.999999999999999 0.0" />
+    </g>
+    <g id="star9" hollow="false" kind="star" sides="9">
+      <polygon points="5.684040286651338 3.120614758428183 8.213938048432697 1.16977778440511 6.732050807568877 4.0 9.92403876506104 4.131759111665348 6.969615506024416 5.34729635533386 9.330127018922195 7.499999999999999 6.285575219373079 6.532088886237956 6.710100716628345 9.69846310392954 5.0 7.0 3.289899283371657 9.698463103929543 3.7144247806269224 6.532088886237957 0.6698729810778081 7.500000000000002 3.0303844939755837 5.347296355333861 0.07596123493895934 4.1317591116653505 3.267949192431122 4.000000000000002 1.786061951567302 1.169777784405111 4.315959713348663 3.120614758428183 4.999999999999999 0.0" />
+    </g>
+    <g id="star10" hollow="false" kind="star" sides="10">
+      <polygon points="5.618033988749895 3.0978869674096927 7.938926261462366 0.9549150281252627 6.618033988749895 3.8244294954150537 9.755282581475768 3.4549150281252627 7.0 5.0 9.755282581475768 6.545084971874736 6.618033988749895 6.175570504584946 7.938926261462367 9.045084971874736 5.618033988749895 6.902113032590307 5.000000000000001 10.0 4.381966011250106 6.902113032590307 2.061073738537635 9.045084971874738 3.381966011250105 6.175570504584947 0.24471741852423268 6.545084971874738 3.0 5.0 0.2447174185242318 3.4549150281252636 3.381966011250105 3.824429495415054 2.061073738537633 0.9549150281252636 4.381966011250105 3.0978869674096927 4.999999999999999 0.0" />
+    </g>
+  </defs>
+  <!-- A palette of symbols. -->
+  <use x="2" y="2" transform="scale(5)" stroke="black" fill="red" href="#square" />
+  <use x="14" y="2" transform="scale(5)" stroke="black" fill="red" href="#plus" />
+  <use x="26" y="2" transform="scale(5)" stroke="black" fill="red" href="#circle" />
+  <use x="38" y="2" transform="scale(5)" stroke="black" fill="red" href="#arc" />
+  <use x="50" y="2" transform="scale(5)" stroke="black" fill="red" href="#arcDown" />
+  <use x="62" y="2" transform="scale(5)" stroke="black" fill="red" href="#cross" />
+  <use x="2" y="14" transform="scale(5)" stroke="black" fill="red" href="#arrow" />
+  <use x="14" y="14" transform="scale(5)" stroke="black" fill="red" href="#line" />
+  <use x="26" y="14" transform="scale(5)" stroke="black" fill="red" href="#male" />
+  <use x="38" y="14" transform="scale(5)" stroke="black" fill="red" href="#female" />
+  <use x="50" y="14" transform="scale(5)" stroke="black" fill="red" href="#bowtie" />
+  <use x="62" y="14" transform="scale(5)" stroke="black" fill="red" href="#ibeam" />
+  <use x="2" y="26" transform="scale(5)" stroke="black" fill="red" href="#roundRectangle" />
+  <use x="14" y="26" transform="scale(5)" stroke="black" fill="red" href="#flower3" />
+  <use x="26" y="26" transform="scale(5)" stroke="black" fill="red" href="#flower4" />
+  <use x="38" y="26" transform="scale(5)" stroke="black" fill="red" href="#flower5" />
+  <use x="50" y="26" transform="scale(5)" stroke="black" fill="red" href="#flower6" />
+  <use x="62" y="26" transform="scale(5)" stroke="black" fill="red" href="#flower7" />
+  <use x="2" y="38" transform="scale(5)" stroke="black" fill="red" href="#flower8" />
+  <use x="14" y="38" transform="scale(5)" stroke="black" fill="red" href="#flower9" />
+  <use x="26" y="38" transform="scale(5)" stroke="black" fill="red" href="#flower10" />
+  <use x="38" y="38" transform="scale(5)" stroke="black" fill="red" href="#triangle" />
+  <use x="50" y="38" transform="scale(5)" stroke="black" fill="red" href="#pentagon" />
+  <use x="62" y="38" transform="scale(5)" stroke="black" fill="red" href="#hexagon" />
+  <use x="2" y="50" transform="scale(5)" stroke="black" fill="red" href="#heptagon" />
+  <use x="14" y="50" transform="scale(5)" stroke="black" fill="red" href="#octagon" />
+  <use x="26" y="50" transform="scale(5)" stroke="black" fill="red" href="#nonagon" />
+  <use x="38" y="50" transform="scale(5)" stroke="black" fill="red" href="#decagon" />
+  <use x="50" y="50" transform="scale(5)" stroke="black" fill="red" href="#star3" />
+  <use x="62" y="50" transform="scale(5)" stroke="black" fill="red" href="#star4" />
+  <use x="2" y="62" transform="scale(5)" stroke="black" fill="red" href="#star5" />
+  <use x="14" y="62" transform="scale(5)" stroke="black" fill="red" href="#star6" />
+  <use x="26" y="62" transform="scale(5)" stroke="black" fill="red" href="#star7" />
+  <use x="38" y="62" transform="scale(5)" stroke="black" fill="red" href="#star8" />
+  <use x="50" y="62" transform="scale(5)" stroke="black" fill="red" href="#star9" />
+  <use x="62" y="62" transform="scale(5)" stroke="black" fill="red" href="#star10" />
+</svg>
diff --git a/rust/pspp/src/spv/read/graph/symbols/.gitignore b/rust/pspp/src/spv/read/graph/symbols/.gitignore
new file mode 100644 (file)
index 0000000..3100bf1
--- /dev/null
@@ -0,0 +1 @@
+data.rs
diff --git a/rust/pspp/src/spv/read/graph/var_set.rs b/rust/pspp/src/spv/read/graph/var_set.rs
new file mode 100644 (file)
index 0000000..1e520be
--- /dev/null
@@ -0,0 +1,670 @@
+use std::{
+    borrow::Cow,
+    collections::{BTreeMap, BTreeSet},
+    iter::{Peekable, chain},
+    ops::{Not as _, RangeInclusive},
+    str::FromStr,
+};
+
+use either::Either;
+use itertools::Itertools as _;
+
+use crate::spv::read::graph::{Series, format::Formatter};
+
+/// Operator precedence:
+///
+/// - `/` (highest)
+/// - `*`, `.` (middle)
+/// - `+` (lowest)
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Operator {
+    /// `+`
+    Blend,
+    /// `*`
+    Cross,
+    /// `/`
+    Nest,
+    /// `.`
+    Dot,
+}
+
+impl Operator {
+    pub fn apply(&self, operands: impl IntoIterator<Item = Expression>) -> Expression {
+        operands
+            .into_iter()
+            .reduce(|a, b| Expression::new_operation(*self, a, b))
+            .unwrap_or_default()
+    }
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub enum Expression {
+    #[default]
+    Unity,
+    Variable(String),
+    Operation {
+        operator: Operator,
+        operands: Box<[Expression; 2]>,
+    },
+}
+
+#[derive(Debug)]
+pub enum VarSetError {
+    UnknownVariable(String),
+    InvalidNest,
+}
+
+impl Expression {
+    pub fn new_variable(name: impl Into<String>) -> Self {
+        Self::Variable(name.into())
+    }
+    pub fn new_operation(operator: Operator, operand1: Expression, operand2: Expression) -> Self {
+        Self::Operation {
+            operator,
+            operands: Box::new([operand1, operand2]),
+        }
+    }
+    pub fn is_unity(&self) -> bool {
+        matches!(self, Self::Unity)
+    }
+    pub fn to_var_set(&self, series: &BTreeMap<&str, Series>) -> Result<VarSet, VarSetError> {
+        match self {
+            Expression::Unity => Ok(VarSet::new_unity()),
+            Expression::Variable(name) => {
+                let series = series
+                    .get(name.as_str())
+                    .ok_or_else(|| VarSetError::UnknownVariable(name.clone()))?;
+                Ok(VarSet::for_variable(series))
+            }
+            Expression::Operation { operator, operands } => {
+                let a = operands[0].to_var_set(series)?;
+                let b = operands[1].to_var_set(series)?;
+                Ok(VarSet::for_operation(*operator, a, b))
+            }
+        }
+    }
+}
+
+/// Error parsing an [Expression].
+#[derive(Clone, Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
+pub enum ExpressionParseError {
+    /// Empty expression.
+    EmptyString,
+    /// Expression was following by garbage.
+    TrailingGarbage,
+    /// Expression missing right parenthesis.
+    ExpectedRightParen,
+    /// Syntax error parsing expression.
+    SyntaxError,
+    /// Unexpected end of expression.
+    UnexpectedEof,
+}
+
+impl FromStr for Expression {
+    type Err = ExpressionParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        #[derive(Clone, Debug, PartialEq, Eq)]
+        enum Token {
+            Operator(Operator),
+            LeftParen,
+            RightParen,
+            Semicolon,
+            Unity,
+            Id(String),
+        }
+        impl Token {
+            fn from_delim(c: char) -> Option<Self> {
+                match c {
+                    '+' => Some(Token::Operator(Operator::Blend)),
+                    '*' => Some(Token::Operator(Operator::Cross)),
+                    '/' => Some(Token::Operator(Operator::Nest)),
+                    '.' => Some(Token::Operator(Operator::Dot)),
+                    '(' => Some(Token::LeftParen),
+                    ')' => Some(Token::RightParen),
+                    ';' => Some(Token::Semicolon),
+                    _ => None,
+                }
+            }
+            fn is_delim(c: char) -> bool {
+                Self::from_delim(c).is_some()
+            }
+            fn from_id(id: &str) -> Self {
+                if id == "1" {
+                    Token::Unity
+                } else {
+                    Token::Id(id.into())
+                }
+            }
+        }
+
+        struct Tokenizer<'a>(&'a str);
+
+        impl<'a> Iterator for Tokenizer<'a> {
+            type Item = Token;
+
+            fn next(&mut self) -> Option<Self::Item> {
+                let mut iter = self.0.chars();
+                if let Some(delim) = Token::from_delim(iter.next()?) {
+                    self.0 = iter.as_str();
+                    return Some(delim);
+                }
+                loop {
+                    let (id, rest) = self.0.split_at(self.0.len() - iter.as_str().len());
+                    if iter.next().is_none_or(Token::is_delim) {
+                        self.0 = rest;
+                        return Some(Token::from_id(id));
+                    }
+                }
+            }
+        }
+
+        fn match_operator<F>(lexer: &mut Peekable<Tokenizer>, predicate: F) -> Option<Operator>
+        where
+            F: Fn(Operator) -> bool,
+        {
+            lexer.next_if_map(|token| {
+                if let Token::Operator(operator) = token
+                    && predicate(operator)
+                {
+                    Ok(operator)
+                } else {
+                    Err(token)
+                }
+            })
+        }
+
+        fn parse_operator<P, I>(
+            lexer: &mut Peekable<Tokenizer>,
+            predicate: P,
+            inner: I,
+        ) -> Result<Expression, ExpressionParseError>
+        where
+            P: Fn(Operator) -> bool,
+            I: Fn(&mut Peekable<Tokenizer>) -> Result<Expression, ExpressionParseError>,
+        {
+            let mut retval = inner(lexer)?;
+            while let Some(operator) = match_operator(lexer, &predicate) {
+                let rhs = inner(lexer)?;
+                retval = Expression::new_operation(operator, retval, rhs);
+            }
+            Ok(retval)
+        }
+
+        fn blend<'a>(
+            lexer: &mut Peekable<Tokenizer<'a>>,
+        ) -> Result<Expression, ExpressionParseError> {
+            parse_operator(lexer, |op| op == Operator::Blend, cross_dot)
+        }
+
+        fn cross_dot<'a>(
+            lexer: &mut Peekable<Tokenizer<'a>>,
+        ) -> Result<Expression, ExpressionParseError> {
+            parse_operator(
+                lexer,
+                |op| op == Operator::Cross || op == Operator::Dot,
+                nest,
+            )
+        }
+
+        fn nest<'a>(
+            lexer: &mut Peekable<Tokenizer<'a>>,
+        ) -> Result<Expression, ExpressionParseError> {
+            parse_operator(lexer, |op| op == Operator::Nest, primary)
+        }
+
+        fn primary<'a>(
+            lexer: &mut Peekable<Tokenizer<'a>>,
+        ) -> Result<Expression, ExpressionParseError> {
+            match lexer.next() {
+                Some(Token::Unity) => Ok(Expression::Unity),
+                Some(Token::Id(id)) => Ok(Expression::Variable(id)),
+                Some(Token::LeftParen) => {
+                    let retval = blend(lexer)?;
+                    if lexer.next_if_eq(&Token::RightParen).is_none() {
+                        return Err(ExpressionParseError::ExpectedRightParen);
+                    }
+                    Ok(retval)
+                }
+                Some(_) => Err(ExpressionParseError::SyntaxError),
+                None => Err(ExpressionParseError::UnexpectedEof),
+            }
+        }
+
+        let mut lexer = Tokenizer(s).peekable();
+        if lexer.peek().is_none() {
+            return Err(ExpressionParseError::EmptyString);
+        }
+        let expression = blend(&mut lexer)?;
+        if lexer.peek().is_some() {
+            return Err(ExpressionParseError::TrailingGarbage);
+        }
+        Ok(expression)
+    }
+}
+
+#[derive(Clone, Debug)]
+pub enum DomainComponent {
+    Categorical(Vec<String>),
+    Continuous(Option<RangeInclusive<f64>>),
+    Unity,
+}
+
+/// Cannot blend continuous and categorical domains.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
+pub struct BlendError;
+
+impl DomainComponent {
+    pub fn for_variable(series: &Series) -> Self {
+        if series.categorical {
+            Self::Categorical(
+                series
+                    .unique_values
+                    .iter()
+                    .map(|value| value.label(&series.formatter))
+                    .collect(),
+            )
+        } else {
+            Self::Continuous(
+                series
+                    .unique_values
+                    .iter()
+                    .filter_map(|value| value.value.as_number().flatten())
+                    .minmax()
+                    .into_option()
+                    .map(|(min, max)| min..=max),
+            )
+        }
+    }
+
+    pub fn categories(&self) -> impl Iterator<Item = &str> {
+        match self {
+            DomainComponent::Categorical(items) => Either::Left(items.iter().map(|s| s.as_str())),
+            DomainComponent::Unity => Either::Right(["0.5"].into_iter()),
+            DomainComponent::Continuous(_) => {
+                // Avoid the need for a third return type by using the same type
+                // as `Unity` but dropping the first (only) element.
+                Either::Right([""].into_iter().dropping(1))
+            }
+        }
+    }
+
+    pub fn into_categories(self) -> Vec<String> {
+        match self {
+            DomainComponent::Categorical(items) => items,
+            DomainComponent::Continuous(_) => Vec::new(),
+            DomainComponent::Unity => vec!["0.5".into()],
+        }
+    }
+
+    pub fn range(&self) -> Option<RangeInclusive<f64>> {
+        match self {
+            Self::Categorical(items) => items.is_empty().not().then(|| 1.0..=items.len() as f64),
+            Self::Continuous(range) => range.clone(),
+            Self::Unity => Some(0.5..=0.5),
+        }
+    }
+
+    pub fn split(&self, s: &str) -> Option<Self> {
+        if let Self::Categorical(items) = self {
+            Some(Self::Categorical(
+                match items.into_iter().find(|element| s == *element) {
+                    Some(_) => vec![s.into()],
+                    None => Vec::new(),
+                },
+            ))
+        } else {
+            None
+        }
+    }
+
+    pub fn blend(&self, other: &Self) -> Result<Self, BlendError> {
+        fn union(
+            a: &Option<RangeInclusive<f64>>,
+            b: &Option<RangeInclusive<f64>>,
+        ) -> Option<RangeInclusive<f64>> {
+            match (a.clone(), b.clone()) {
+                (Some(a), Some(b)) => {
+                    let (a0, a1) = a.into_inner();
+                    let (b0, b1) = b.into_inner();
+                    Some(a0.min(b0)..=a1.max(b1))
+                }
+                (Some(range), None) | (None, Some(range)) => Some(range),
+                (None, None) => None,
+            }
+        }
+
+        match (self, other) {
+            (Self::Categorical(a), Self::Categorical(b)) => Ok(Self::Categorical(
+                chain(a, b)
+                    .collect::<BTreeSet<_>>()
+                    .into_iter()
+                    .cloned()
+                    .collect(),
+            )),
+            (Self::Continuous(a), Self::Continuous(b)) => Ok(Self::Continuous(union(a, b))),
+            (c @ Self::Categorical(_), Self::Unity) | (Self::Unity, c @ Self::Categorical(_)) => {
+                Ok(c.clone())
+            }
+            (Self::Unity, Self::Unity) => Ok(Self::Unity),
+            (Self::Unity, Self::Continuous(range)) | (Self::Continuous(range), Self::Unity) => {
+                Ok(Self::Continuous(union(range, &Some(0.5..=0.5))))
+            }
+            (Self::Categorical(_), Self::Continuous(_))
+            | (Self::Continuous(_), Self::Categorical(_)) => Err(BlendError),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum Domain {
+    Empty,
+    Component(DomainComponent),
+
+    /// Cross-product domain.
+    ///
+    /// Includes all combinations of the sub-domains.
+    Cross(Box<[Domain; 2]>),
+
+    /// Nest domain.
+    ///
+    /// Nest is a subset of the cross-product of the sub-domains which only
+    /// includes the part of the cross-product that is actually present in the data.
+    Nest {
+        domains: Box<[Domain; 2]>,
+        /// The data, for limiting the cross-product.
+        data: Vec<Series>,
+    },
+
+    Blend(Box<[Domain; 2]>),
+}
+
+impl Domain {
+    pub fn new_unity() -> Self {
+        Self::Component(DomainComponent::Unity)
+    }
+
+    pub fn for_variable(series: &Series) -> Self {
+        Self::Component(DomainComponent::for_variable(series))
+    }
+
+    pub fn cross(a: Domain, b: Domain) -> Self {
+        Self::Cross(Box::new([a, b]))
+    }
+
+    pub fn nest(a: Domain, b: Domain, data: Vec<Series>) -> Self {
+        Self::Nest {
+            domains: Box::new([a, b]),
+            data,
+        }
+    }
+
+    /// The number of domain components in this domain.
+    pub fn len(&self) -> usize {
+        match self {
+            Domain::Empty => 0,
+            Domain::Component(_) => 1,
+            Domain::Cross(domains) | Domain::Nest { domains, .. } => {
+                domains[0].len() + domains[1].len()
+            }
+            Domain::Blend(domains) => domains[0].len(),
+        }
+    }
+
+    /// The `index`th component of this domain.
+    pub fn component(&self, index: usize) -> Option<Cow<'_, DomainComponent>> {
+        match self {
+            Domain::Empty => None,
+            Domain::Component(domain_component) => {
+                (index == 0).then_some(Cow::Borrowed(domain_component))
+            }
+            Domain::Cross(domains) | Domain::Nest { domains, data: _ } => {
+                if let Some(difference) = index.checked_sub(domains[0].len()) {
+                    domains[1].component(difference)
+                } else {
+                    domains[0].component(index)
+                }
+            }
+            Domain::Blend(domains) => {
+                let a = domains[0].component(index)?;
+                let b = domains[1].component(index)?;
+                Some(Cow::Owned(a.blend(&b).ok()?))
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct VarSet {
+    data: Vec<Series>,
+    domain: Domain,
+}
+
+impl VarSet {
+    pub fn len(&self) -> usize {
+        self.data
+            .first()
+            .map_or_default(|series| series.values.len())
+    }
+
+    pub fn new_unity() -> Self {
+        Self {
+            data: vec![Series::new_empty()],
+            domain: Domain::new_unity(),
+        }
+    }
+
+    pub fn for_variable(series: &Series) -> Self {
+        Self {
+            data: vec![series.clone()],
+            domain: Domain::for_variable(series),
+        }
+    }
+
+    pub fn for_operation(operator: Operator, a: Self, b: Self) -> Self {
+        match operator {
+            Operator::Blend => a.blend(b),
+            Operator::Cross => a.cross(b),
+            Operator::Nest => a.nest(b),
+            Operator::Dot => a.dot(b),
+        }
+    }
+
+    pub fn cross(self, other: Self) -> Self {
+        if self.domain.len() == 0 {
+            other
+        } else if other.domain.len() == 0 {
+            self
+        } else {
+            Self {
+                data: chain(self.data, other.data).collect(),
+                domain: Domain::cross(self.domain, other.domain),
+            }
+        }
+    }
+
+    pub fn nest(self, other: Self) -> Self {
+        if self.domain.len() == 0 {
+            other
+        } else if other.domain.len() == 0 {
+            self
+        } else {
+            let data = chain(self.data, other.data).collect_vec();
+            Self {
+                domain: Domain::nest(self.domain, other.domain, data.clone()),
+                data,
+            }
+        }
+    }
+
+    pub fn dot(self, _other: Self) -> Self {
+        todo!()
+    }
+
+    pub fn blend(self, _other: Self) -> Self {
+        todo!()
+    }
+}
+
+pub struct NestError;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum DimensionKind {
+    Coordinate,
+    Facet,
+    Statistic,
+    Weight,
+    Layer,
+}
+
+struct DimensionInfo {
+    name: String,
+    kind: DimensionKind,
+    format: Formatter,
+    is_categorical: bool,
+}
+
+#[cfg(test)]
+mod tests {
+    use std::str::FromStr;
+
+    use crate::spv::read::graph::var_set::{Expression, ExpressionParseError, Operator};
+
+    #[test]
+    fn parse_expression() {
+        assert_eq!(Expression::from_str("1"), Ok(Expression::Unity));
+        assert_eq!(
+            Expression::from_str("abc"),
+            Ok(Expression::new_variable("abc"))
+        );
+        assert_eq!(
+            Expression::from_str("a+1"),
+            Ok(Expression::new_operation(
+                Operator::Blend,
+                Expression::new_variable("a"),
+                Expression::Unity
+            ))
+        );
+        assert_eq!(
+            Expression::from_str("abc"),
+            Ok(Expression::new_variable("abc"))
+        );
+        for (s, operator) in [
+            ("abc+def", Operator::Blend),
+            ("abc*def", Operator::Cross),
+            ("abc/def", Operator::Nest),
+            ("abc.def", Operator::Dot),
+        ] {
+            assert_eq!(
+                Expression::from_str(s),
+                Ok(Expression::new_operation(
+                    operator,
+                    Expression::new_variable("abc"),
+                    Expression::new_variable("def")
+                ))
+            );
+        }
+
+        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
+        enum Side {
+            Left,
+            Right,
+        }
+        use Side::*;
+        for (s, operator1, operator2, higher) in [
+            // All the possible combinations of operators.
+            ("abc+def+ghi", Operator::Blend, Operator::Blend, Left),
+            ("abc*def+ghi", Operator::Cross, Operator::Blend, Left),
+            ("abc/def+ghi", Operator::Nest, Operator::Blend, Left),
+            ("abc.def+ghi", Operator::Dot, Operator::Blend, Left),
+            ("abc+def*ghi", Operator::Blend, Operator::Cross, Right),
+            ("abc*def*ghi", Operator::Cross, Operator::Cross, Left),
+            ("abc/def*ghi", Operator::Nest, Operator::Cross, Left),
+            ("abc.def*ghi", Operator::Dot, Operator::Cross, Left),
+            ("abc+def/ghi", Operator::Blend, Operator::Nest, Right),
+            ("abc*def/ghi", Operator::Cross, Operator::Nest, Right),
+            ("abc/def/ghi", Operator::Nest, Operator::Nest, Left),
+            ("abc.def/ghi", Operator::Dot, Operator::Nest, Right),
+            ("abc+def.ghi", Operator::Blend, Operator::Dot, Right),
+            ("abc*def.ghi", Operator::Cross, Operator::Dot, Left),
+            ("abc/def.ghi", Operator::Nest, Operator::Dot, Left),
+            ("abc.def.ghi", Operator::Dot, Operator::Dot, Left),
+            // Adding parentheses that match the default precedence.
+            ("(abc+def)+ghi", Operator::Blend, Operator::Blend, Left),
+            ("(abc*def)+ghi", Operator::Cross, Operator::Blend, Left),
+            ("(abc/def)+ghi", Operator::Nest, Operator::Blend, Left),
+            ("(abc.def)+ghi", Operator::Dot, Operator::Blend, Left),
+            ("abc+(def*ghi)", Operator::Blend, Operator::Cross, Right),
+            ("(abc*def)*ghi", Operator::Cross, Operator::Cross, Left),
+            ("(abc/def)*ghi", Operator::Nest, Operator::Cross, Left),
+            ("(abc.def)*ghi", Operator::Dot, Operator::Cross, Left),
+            ("abc+(def/ghi)", Operator::Blend, Operator::Nest, Right),
+            ("abc*(def/ghi)", Operator::Cross, Operator::Nest, Right),
+            ("(abc/def)/ghi", Operator::Nest, Operator::Nest, Left),
+            ("abc.(def/ghi)", Operator::Dot, Operator::Nest, Right),
+            ("abc+(def.ghi)", Operator::Blend, Operator::Dot, Right),
+            ("(abc*def).ghi", Operator::Cross, Operator::Dot, Left),
+            ("(abc/def).ghi", Operator::Nest, Operator::Dot, Left),
+            ("(abc.def).ghi", Operator::Dot, Operator::Dot, Left),
+            // Adding parentheses that reverse the default precedence.
+            ("abc+(def+ghi)", Operator::Blend, Operator::Blend, Right),
+            ("abc*(def+ghi)", Operator::Cross, Operator::Blend, Right),
+            ("abc/(def+ghi)", Operator::Nest, Operator::Blend, Right),
+            ("abc.(def+ghi)", Operator::Dot, Operator::Blend, Right),
+            ("(abc+def)*ghi", Operator::Blend, Operator::Cross, Left),
+            ("abc*(def*ghi)", Operator::Cross, Operator::Cross, Right),
+            ("abc/(def*ghi)", Operator::Nest, Operator::Cross, Right),
+            ("abc.(def*ghi)", Operator::Dot, Operator::Cross, Right),
+            ("(abc+def)/ghi", Operator::Blend, Operator::Nest, Left),
+            ("(abc*def)/ghi", Operator::Cross, Operator::Nest, Left),
+            ("abc/(def/ghi)", Operator::Nest, Operator::Nest, Right),
+            ("(abc.def)/ghi", Operator::Dot, Operator::Nest, Left),
+            ("(abc+def).ghi", Operator::Blend, Operator::Dot, Left),
+            ("abc*(def.ghi)", Operator::Cross, Operator::Dot, Right),
+            ("abc/(def.ghi)", Operator::Nest, Operator::Dot, Right),
+            ("abc.(def.ghi)", Operator::Dot, Operator::Dot, Right),
+        ] {
+            let operand1 = Expression::new_variable("abc");
+            let operand2 = Expression::new_variable("def");
+            let operand3 = Expression::new_variable("ghi");
+            let expected = match higher {
+                Left => Expression::new_operation(
+                    operator2,
+                    Expression::new_operation(operator1, operand1, operand2),
+                    operand3,
+                ),
+                Right => Expression::new_operation(
+                    operator1,
+                    operand1,
+                    Expression::new_operation(operator2, operand2, operand3),
+                ),
+            };
+            assert_eq!(
+                Expression::from_str(s),
+                Ok(expected),
+                "{s}, {operator1:?}, {operator2:?}, {higher:?}"
+            );
+        }
+
+        assert_eq!(
+            Expression::from_str(""),
+            Err(ExpressionParseError::EmptyString)
+        );
+        assert_eq!(
+            Expression::from_str("x)"),
+            Err(ExpressionParseError::TrailingGarbage)
+        );
+        assert_eq!(
+            Expression::from_str("(x"),
+            Err(ExpressionParseError::ExpectedRightParen)
+        );
+        assert_eq!(
+            Expression::from_str("+"),
+            Err(ExpressionParseError::SyntaxError)
+        );
+        assert_eq!(
+            Expression::from_str("x+"),
+            Err(ExpressionParseError::UnexpectedEof)
+        );
+    }
+}
index 8592252f72a6c88dde65285af3fa411c0b50616d..d5b6fc7fca70fb6ba6bc6e65986d4b7aa16196f9 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -322,7 +322,7 @@ impl Markup {
                 .create_element("font")
                 .with_attributes(
                     attributes
-                        .into_iter()
+                        .iter()
                         .map(|(name, value)| (*name, Cow::from(value))),
                 )
                 .write_inner_content(|w| self.write_styles(w, elements, &[]))?;
@@ -376,9 +376,9 @@ impl Markup {
                     child.to_pango_inner(expand, s, attrs);
                 }
             }
-            Markup::Text(string) => s.push_str(&string),
+            Markup::Text(string) => s.push_str(string),
             Markup::Variable(variable) => match expand(*variable) {
-                Some(value) => s.push_str(&*value),
+                Some(value) => s.push_str(&value),
                 None => write!(s, "&[{variable}]").unwrap(),
             },
             Markup::Style { style, child } => {
@@ -395,7 +395,7 @@ impl Markup {
                     }
                     Style::Underline => AttrInt::new_underline(pango::Underline::Single).upcast(),
                     Style::Strike => AttrInt::new_strikethrough(true).upcast(),
-                    Style::Face(face) => AttrString::new_family(&face).upcast(),
+                    Style::Face(face) => AttrString::new_family(face).upcast(),
                     Style::Color(color) => {
                         let (r, g, b) = color.into_rgb16();
                         AttrColor::new_foreground(r, g, b).upcast()
@@ -410,9 +410,7 @@ impl Markup {
     }
 
     fn parse_variables(&self) -> Option<Vec<Markup>> {
-        let Some(mut s) = self.as_text() else {
-            return None;
-        };
+        let mut s = self.as_text()?;
         let mut results = Vec::new();
         let mut offset = 0;
         while let Some(start) = s[offset..].find("&[").map(|pos| pos + offset)
@@ -445,7 +443,7 @@ impl Markup {
         if let Some(Markup::Text(head)) = a.last_mut()
             && let Some(Markup::Text(tail)) = b.first()
         {
-            head.push_str(&tail);
+            head.push_str(tail);
             a.extend(b.drain(1..));
         } else {
             a.append(&mut b);
@@ -517,7 +515,7 @@ impl Block {
                     Style::Strike => strike = true,
                     Style::Emphasis => font_style.italic = true,
                     Style::Strong => font_style.bold = true,
-                    Style::Face(face) => font_style.font = face,
+                    Style::Face(face) => font_style.font = Cow::from(face),
                     Style::Color(color) => font_style.fg = color,
                     Style::Size(points) => font_style.size = points as i32,
                 };
@@ -815,7 +813,7 @@ fn parse_nodes(nodes: &[Node]) -> Markup {
         if let Markup::Text(suffix) = &markup
             && let Some(Markup::Text(last)) = dst.last_mut()
         {
-            last.push_str(&suffix);
+            last.push_str(suffix);
         } else {
             dst.push(markup);
         }
@@ -843,7 +841,7 @@ fn parse_nodes(nodes: &[Node]) -> Markup {
                 } else {
                     text
                 };
-                add_markup(&mut retval, Markup::Text(unescape(&text).into_owned()));
+                add_markup(&mut retval, Markup::Text(unescape(text).into_owned()));
             }
             // SPSS often starts paragraphs with an initial `<BR>` that it
             // ignores, but it does honor `<br>`.  So weird.
@@ -870,12 +868,12 @@ fn parse_nodes(nodes: &[Node]) -> Markup {
                             apply_style(&mut inner, Style::Face(face.clone()));
                         }
                         if let Some(Some(color)) = element.attributes.get("color")
-                            && let Ok(color) = Color::from_str(&color)
+                            && let Ok(color) = Color::from_str(color)
                         {
                             apply_style(&mut inner, Style::Color(color));
                         }
                         if let Some(Some(html_size)) = element.attributes.get("size")
-                            && let Ok(html_size) = usize::from_str(&html_size)
+                            && let Ok(html_size) = usize::from_str(html_size)
                             && let Some(index) = html_size.checked_sub(1)
                             && let Some(points) =
                                 [6.0, 7.5, 9.0, 10.5, 13.5, 18.0, 27.0].get(index).copied()
index aa67d75402bbc591512e2dbd895cf20628776ded..3631c6613195909cc9054404d547c3844dbb5d55 100644 (file)
 //! Legacy binary data.
-use std::io::{Read, Seek, SeekFrom};
+use core::f64;
+use std::{
+    fmt::Display,
+    io::{Cursor, Read, Seek, SeekFrom, Write},
+    iter::zip,
+};
 
-use binrw::{BinRead, BinResult, binread};
+use binrw::{BinRead, BinResult, BinWrite, Endian, binread, binwrite};
 use displaydoc::Display;
 use encoding_rs::UTF_8;
 use indexmap::IndexMap;
+use itertools::Itertools as _;
+use quick_xml::events::BytesText;
 
 use crate::{
     data::Datum,
-    spv::read::light::{U32String, parse_vec},
+    output::pivot::{Axis3, Dimension, Group, Leaf, PivotTable, value::Value},
+    spv::{
+        read::{
+            legacy_xml::datum_as_format,
+            light::{U32String, parse_vec},
+        },
+        write::SpvString,
+    },
 };
 
+/// A collection of named [DataSource]s for a table or graph.
+#[derive(Clone, Debug, Default)]
+pub struct DataSources(pub IndexMap<String, DataSource>);
+
+impl DataSources {
+    /// Encodes `data` into the legacy detail member format.
+    ///
+    /// # Panic
+    ///
+    /// Panics if `data` is empty.
+    pub fn to_legacy_bin(&self) -> Vec<u8> {
+        assert!(!self.0.is_empty());
+        let mut bin = Cursor::new(Vec::new());
+        bin.set_position(8);
+
+        /// Starting offset of the first metadata record.
+        const METADATA_OFFSET: usize = 8;
+        /// Size of an individual metadata record
+        const METADATA_SIZE: usize = 4 + 4 + 4 + 64 + 4;
+
+        let mut data_offset = METADATA_OFFSET + self.0.len() * METADATA_SIZE;
+        for (source_name, source) in &self.0 {
+            #[binwrite]
+            #[br(little)]
+            #[derive(Debug)]
+            struct Metadata<'a> {
+                n_values: u32,
+                n_variables: u32,
+                data_offset: u32,
+                #[bw(write_with(write_fixed_utf8_string), args(64))]
+                source_name: &'a str,
+                #[bw(magic(0u32))]
+                x: (),
+            }
+            let metadata = Metadata {
+                n_values: source
+                    .0
+                    .values()
+                    .next()
+                    .map_or(0, |series| series.data.len()) as u32,
+                n_variables: source.0.len() as u32,
+                data_offset: data_offset as u32,
+                source_name: source_name.as_str(),
+                x: (),
+            };
+            metadata.write_le(&mut bin).unwrap();
+
+            for series in source.0.values() {
+                data_offset += 288 + 8 * series.data.len();
+            }
+        }
+
+        let mut n_strings = Vec::with_capacity(self.0.len());
+        for source in self.0.values() {
+            let mut variable_strings = Vec::with_capacity(source.0.len());
+            for (variable_name, series) in &source.0 {
+                write_fixed_utf8_string(&variable_name.as_str(), &mut bin, Endian::Little, (288,))
+                    .unwrap();
+                for datum in &series.data {
+                    datum
+                        .as_number()
+                        .flatten()
+                        .unwrap_or(-f64::MAX)
+                        .write_le(&mut bin)
+                        .unwrap();
+                }
+                variable_strings.push(series.data.iter().filter(|datum| datum.is_string()).count());
+            }
+            n_strings.push(variable_strings);
+        }
+
+        let n_maps = n_strings
+            .iter()
+            .filter(|variable_strings| variable_strings.iter().any(|n| *n != 0))
+            .count() as u32;
+        let mut labels = IndexMap::<&str, u32>::new();
+        if n_maps > 0 {
+            let mut count = 0;
+            n_maps.write_le(&mut bin).unwrap();
+            for ((source_name, source), variable_strings) in self.0.iter().zip(&n_strings) {
+                let Some((_n, last_string)) = variable_strings
+                    .iter()
+                    .zip(1u32..)
+                    .filter(|(n, _index)| **n > 0)
+                    .last()
+                else {
+                    continue;
+                };
+                count += 1;
+                (SpvString(source_name), last_string)
+                    .write_le(&mut bin)
+                    .unwrap();
+                for ((variable_name, series), n_strings) in source
+                    .0
+                    .iter()
+                    .zip(variable_strings)
+                    .take(last_string as usize)
+                {
+                    (SpvString(variable_name), *n_strings as u32)
+                        .write_le(&mut bin)
+                        .unwrap();
+                    for (index, datum) in series.data.iter().enumerate() {
+                        if let Some(string) = datum.as_string() {
+                            let entry = labels.entry(string.as_str());
+                            let label_index = entry.index();
+                            *entry.or_default() += 1;
+                            (index as u32, label_index as u32)
+                                .write_le(&mut bin)
+                                .unwrap();
+                        }
+                    }
+                }
+            }
+            assert_eq!(n_maps, count);
+
+            (labels.len() as u32).write_le(&mut bin).unwrap();
+            for (label, frequency) in &labels {
+                (*frequency, SpvString(label)).write_le(&mut bin).unwrap();
+            }
+        }
+
+        #[binwrite]
+        #[bw(little)]
+        struct Header {
+            #[bw(magic(0u8))]
+            version: u8,
+            n_sources: u16,
+            member_size: u32,
+        }
+        let header = Header {
+            version: 0xb0,
+            n_sources: self.0.len() as u16,
+            member_size: bin.position() as u32,
+        };
+        bin.set_position(0);
+        header.write_le(&mut bin).unwrap();
+
+        bin.into_inner()
+    }
+
+    /// Decodes legacy binary data into a map from a series name to a map of
+    /// variables, which in turn contains a vector of [Datum]s.
+    pub fn from_legacy_bin(
+        bin: &[u8],
+        warn: &mut dyn FnMut(LegacyBinWarning),
+    ) -> Result<DataSources, binrw::Error> {
+        LegacyBin::from_data(bin, warn)
+    }
+
+    /// Renders these data sources as a pivot table.
+    pub fn as_pivot_table(&self) -> PivotTable {
+        let n_values = self
+            .0
+            .values()
+            .flat_map(|map| map.0.values())
+            .map(|values| values.data.len())
+            .max()
+            .unwrap_or(0);
+        let index = Dimension::new(
+            Group::new("Index")
+                .with_multiple(Leaf::numbers(0..n_values))
+                .with_label_shown(),
+        );
+        let variables = Dimension::new(Group::new("Variables").with_multiple(self.0.iter().map(
+            |(name, contents)| {
+                Group::new(name.as_str()).with_multiple(contents.0.keys().map(|name| {
+                    name.replace("categories", "\ncategories")
+                        .replace("labels", "\nlabels")
+                        .replace("group", "\ngroup")
+                        .replace("Label", "\nLabel")
+                }))
+            },
+        )));
+        let mut pivot_table = PivotTable::new([(Axis3::Y, index), (Axis3::X, variables)]);
+        for (variable_index, (variable_name, values)) in
+            self.0.values().flat_map(|map| map.0.iter()).enumerate()
+        {
+            for (value_index, data_value) in values.data.iter().enumerate() {
+                let value = Value::new_datum(data_value).with_value_label(
+                    (variable_name == "cellFormat")
+                        .then(|| datum_as_format(data_value).to_string()),
+                );
+                pivot_table.insert([value_index, variable_index], value);
+            }
+        }
+        pivot_table
+    }
+}
+
+/// A collection of named [DataSeries] in [DataSources].
+///
+/// The series in a single `DataSource` should all have the same number of data
+/// values; if not, then they cannot be written to a legacy binary data file.
+#[derive(Clone, Debug, Default)]
+pub struct DataSource(pub IndexMap<String, DataSeries>);
+
+impl DataSource {
+    /// Writes this `DataSource` to [quick_xml] writer `writer` as an
+    /// `embeddedSource` element, using `name` as the element's `id`.
+    pub fn write_as_embedded_source<W>(
+        &self,
+        writer: &mut quick_xml::Writer<W>,
+        name: &str,
+    ) -> std::io::Result<()>
+    where
+        W: Write,
+    {
+        struct SemicolonEscape<'a>(&'a str);
+
+        impl<'a> Display for SemicolonEscape<'a> {
+            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+                let mut s = self.0;
+                while let Some((head, tail)) = s.split_once(['&', '<', ';']) {
+                    f.write_str(head)?;
+                    let escaped = match s.as_bytes()[head.len()] {
+                        b'&' => "&amp;",
+                        b'<' => "&gt;",
+                        b';' => "\\;",
+                        _ => unreachable!(),
+                    };
+                    f.write_str(escaped)?;
+                    s = tail;
+                }
+                f.write_str(s)
+            }
+        }
+
+        writer
+            .create_element("embeddedSource")
+            .with_attribute(("id", name))
+            .write_inner_content(|w| {
+                w.create_element("names")
+                    .write_text_content(BytesText::new(&self.0.keys().join(";")))?;
+                w.create_element("types")
+                    .write_text_content(BytesText::new(
+                        &self
+                            .0
+                            .values()
+                            .map(|series| {
+                                if series.data.iter().all(|datum| datum.is_string()) {
+                                    "string"
+                                } else {
+                                    "double"
+                                }
+                            })
+                            .join(";"),
+                    ))?;
+                if self.0.values().any(|series| series.label.is_some()) {
+                    w.create_element("labels").write_inner_content(|w| {
+                        let w = w.get_mut();
+                        for (column, series) in self.0.values().enumerate() {
+                            if column > 0 {
+                                w.write_all(b";")?;
+                            }
+                            if let Some(label) = &series.label {
+                                write!(w, "{}", SemicolonEscape(label))?;
+                            }
+                        }
+                        Ok(())
+                    })?;
+                }
+                let rows = self
+                    .0
+                    .values()
+                    .map(|series| series.data.len())
+                    .max()
+                    .unwrap_or_default();
+                let empty = Datum::String(String::new());
+                for row in 0..rows {
+                    w.create_element("row").write_inner_content(|w| {
+                        let w = w.get_mut();
+                        for (column, series) in self.0.values().enumerate() {
+                            if column > 0 {
+                                w.write_all(b";")?;
+                            }
+                            let datum = series.data.get(row).unwrap_or(&empty);
+                            match datum {
+                                Datum::Number(None) => (),
+                                Datum::Number(Some(number)) => write!(w, "{number}")?,
+                                Datum::String(string) => write!(w, "{}", SemicolonEscape(string))?,
+                            }
+                        }
+                        Ok(())
+                    })?;
+                }
+                Ok(())
+            })?;
+        Ok(())
+    }
+
+    /// Parses `csv` as a `DataSource` in CSV format.
+    pub fn from_csv_data(csv: &[u8]) -> Result<Self, csv::Error> {
+        let mut reader = csv::Reader::from_reader(Cursor::new(&csv));
+        let headers = reader.headers()?;
+        let mut variables = vec![DataSeries::default(); headers.len()];
+        for row in reader.records() {
+            let row = row?;
+            for (column, s) in row.iter().enumerate() {
+                let value = match s.parse::<f64>() {
+                    Ok(number) => Datum::Number(Some(number)),
+                    Err(_) => Datum::String(s.into()),
+                };
+                variables[column].data.push(value);
+            }
+        }
+        Ok(DataSource(
+            zip(
+                reader.headers().unwrap().iter().map(|name| name.into()),
+                variables,
+            )
+            .collect(),
+        ))
+    }
+}
+
+/// A single column of data with an optional label.
+#[derive(Clone, Debug, Default)]
+pub struct DataSeries {
+    /// Label.
+    ///
+    /// Data read from a [LegacyBin] never has a label.
+    pub label: Option<String>,
+
+    /// Data.
+    pub data: Vec<Datum<String>>,
+}
+
 /// A warning decoding a legacy binary member.
 #[derive(Clone, Debug, Display, thiserror::Error)]
 pub enum LegacyBinWarning {
@@ -73,24 +414,24 @@ pub struct LegacyBin {
 impl LegacyBin {
     /// Decodes legacy binary data into a map from a series name to a map of
     /// variables, which in turn contains a vector of [Datum]s.
-    pub fn decode(
-        &self,
-        warn: &mut dyn FnMut(LegacyBinWarning),
-    ) -> IndexMap<String, IndexMap<String, Vec<Datum<String>>>> {
+    pub fn decode(&self, warn: &mut dyn FnMut(LegacyBinWarning)) -> DataSources {
         let mut sources = IndexMap::new();
         for (metadata, data) in self.metadata.iter().zip(&self.data) {
             let mut variables = IndexMap::new();
             for variable in &data.variables {
                 variables.insert(
                     variable.variable_name.clone(),
-                    variable
-                        .values
-                        .iter()
-                        .map(|value| Datum::Number((*value != f64::MIN).then_some(*value)))
-                        .collect::<Vec<_>>(),
+                    DataSeries {
+                        label: None,
+                        data: variable
+                            .values
+                            .iter()
+                            .map(|value| Datum::Number((*value != f64::MIN).then_some(*value)))
+                            .collect::<Vec<_>>(),
+                    },
                 );
             }
-            sources.insert(metadata.source_name.clone(), variables);
+            sources.insert(metadata.source_name.clone(), DataSource(variables));
         }
         if let Some(strings) = &self.strings {
             for map in &strings.source_maps {
@@ -99,7 +440,7 @@ impl LegacyBin {
                     continue;
                 };
                 for var_map in &map.variable_maps {
-                    let Some(variable) = source.get_mut(&var_map.variable_name) else {
+                    let Some(variable) = source.0.get_mut(&var_map.variable_name) else {
                         warn(LegacyBinWarning::UnknownVariable {
                             source_name: map.source_name.clone(),
                             variable: var_map.variable_name.clone(),
@@ -117,13 +458,13 @@ impl LegacyBin {
                             });
                             continue;
                         };
-                        let Some(value) = variable.get_mut(datum_map.value_idx) else {
+                        let Some(value) = variable.data.get_mut(datum_map.value_idx) else {
                             warn(LegacyBinWarning::OutOfRangeValueIdx {
                                 source_name: map.source_name.clone(),
                                 variable: var_map.variable_name.clone(),
                                 datum_idx,
                                 value_idx: datum_map.value_idx,
-                                n_values: variable.len(),
+                                n_values: variable.data.len(),
                             });
                             continue;
                         };
@@ -132,7 +473,15 @@ impl LegacyBin {
                 }
             }
         }
-        sources
+        DataSources(sources)
+    }
+
+    /// Decodes legacy binary data from `data`.
+    pub fn from_data(
+        data: &[u8],
+        warn: &mut dyn FnMut(LegacyBinWarning),
+    ) -> Result<DataSources, binrw::Error> {
+        Ok(Self::read(&mut Cursor::new(&data))?.decode(warn))
     }
 }
 
@@ -292,3 +641,66 @@ fn parse_fixed_utf8_string(n: usize) -> BinResult<String> {
     }
     Ok(UTF_8.decode_without_bom_handling(&buf).0.into_owned())
 }
+
+/// Writes a UTF-8 string, padded it on the right with 0-bytes to length `n`.
+fn write_fixed_utf8_string<W>(
+    s: &&str,
+    writer: &mut W,
+    _endian: Endian,
+    (n,): (usize,),
+) -> BinResult<()>
+where
+    W: Write + Seek,
+{
+    writer.write_all(&s.as_bytes()[..n.min(s.len())])?;
+    for _ in s.len()..n {
+        writer.write_all(&[0])?;
+    }
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use std::{fmt::Write, path::Path};
+
+    use crate::{output::pivot::tests::assert_lines_eq, spv::legacy_bin::LegacyBin};
+
+    fn test_legacy_data_file(name: &str) {
+        let input_filename = Path::new("src/spv/testdata")
+            .join(name)
+            .with_extension("bin");
+        let expected_filename = input_filename.with_extension("expected");
+        let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+
+        let mut actual = String::new();
+        let data = std::fs::read(&input_filename).unwrap();
+        let data =
+            LegacyBin::from_data(&data, &mut |w| writeln!(&mut actual, "{w}").unwrap()).unwrap();
+        write!(&mut actual, "{}", data.as_pivot_table()).unwrap();
+
+        if expected != actual {
+            if std::env::var("PSPP_REFRESH_EXPECTED").is_ok() {
+                std::fs::write(&expected_filename, actual).unwrap();
+                panic!("{}: refreshed output", expected_filename.display());
+            } else {
+                eprintln!("note: rerun with PSPP_REFRESH_EXPECTED=1 to refresh expected output");
+            }
+        }
+        assert_lines_eq(&expected, expected_filename.display(), &actual, "actual");
+
+        let written = data.to_legacy_bin();
+        let mut reread = String::new();
+        let reread_data =
+            LegacyBin::from_data(&written, &mut |w| writeln!(&mut reread, "{w}").unwrap()).unwrap();
+        write!(&mut reread, "{}", reread_data.as_pivot_table()).unwrap();
+        if expected != reread {
+            eprintln!("wrote and reread legacy data differs from expected:");
+            assert_lines_eq(&expected, expected_filename.display(), &reread, "actual");
+        }
+    }
+
+    #[test]
+    fn test_legacy_data_1() {
+        test_legacy_data_file("legacy-data1");
+    }
+}
index 20a9fb868a55436866543dcec09de773879c670d..31ce908dd58c5e811f8522e3b2f834ebe4ed6272 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -29,7 +29,6 @@ use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
 use displaydoc::Display;
 use enum_map::{Enum, EnumMap};
 use hashbrown::HashSet;
-use indexmap::IndexMap;
 use ordered_float::OrderedFloat;
 use serde::Deserialize;
 
@@ -41,13 +40,15 @@ use crate::{
         look::{self, Area, AreaStyle, CellStyle, Color, HorzAlign, Look, RowParity, VertAlign},
         value::{Value, ValueFormat},
     },
-    spv::read::light::decode_format,
+    spv::{legacy_bin::DataSources, read::light::decode_format},
 };
 
 /// Interprets this data value as a [Format], first by looking it up in
 /// `format_map` and otherwise by interpreting it as a [Format] directly.
 ///
 /// This should probably be a method on some hypothetical FormatMap.
+///
+/// [Format]: crate::format::Format
 pub fn datum_as_format(datum: &Datum<String>) -> crate::format::Format {
     let f = match datum {
         Datum::Number(Some(number)) => *number as i64,
@@ -70,7 +71,7 @@ fn datum_as_pivot_value(datum: &Datum<String>, format: crate::format::Format) ->
     {
         Value::new_time(time)
     } else if !datum.is_sysmis() {
-        Value::new_datum(&datum)
+        Value::new_datum(datum)
     } else {
         Value::new_empty()
     }
@@ -214,7 +215,7 @@ impl Visualization {
     }
     pub fn decode_series(
         &self,
-        data: IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
+        data: DataSources,
         warn: &mut dyn FnMut(LegacyXmlWarning),
     ) -> BTreeMap<&str, Series> {
         let mut variables: Vec<&dyn Variable> = self
@@ -273,7 +274,7 @@ impl Visualization {
             }
             footnotes.push(
                 pivot::Footnote::new(footnote.content)
-                    .with_marker(footnote.marker.map(|s| Value::new_user_text(s))),
+                    .with_marker(footnote.marker.map(Value::new_user_text)),
             );
         }
         pivot::Footnotes::from_iter(footnotes)
@@ -300,11 +301,11 @@ impl Visualization {
         for (axis, dimension) in graph.faceting.dimensions() {
             let dim_series = decode_axis_dimensions(
                 dimension.iter().copied(),
-                &series,
+                series,
                 &axes,
-                &styles,
+                styles,
                 axis,
-                &footnotes,
+                footnotes,
                 level_ofs,
                 &mut dims,
             );
@@ -313,7 +314,7 @@ impl Visualization {
                     .into_iter()
                     .map(|series| {
                         let name = &series.name;
-                        let coordinate = graph.faceting.layer_value(&name).unwrap();
+                        let coordinate = graph.faceting.layer_value(name).unwrap();
                         series
                             .coordinate_to_index
                             .borrow()
@@ -332,9 +333,8 @@ impl Visualization {
 
     fn graph(&self) -> Result<&Graph, super::Error> {
         for child in &self.children {
-            match child {
-                VisChild::Graph(g) => return Ok(g),
-                _ => (),
+            if let VisChild::Graph(g) = child {
+                return Ok(g);
             }
         }
         Err(super::Error::LegacyMissingGraph)
@@ -342,7 +342,7 @@ impl Visualization {
 
     pub fn decode(
         &self,
-        binary_data: IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
+        binary_data: DataSources,
         look: Look,
         warn: &mut dyn FnMut(LegacyXmlWarning),
     ) -> Result<PivotTable, super::Error> {
@@ -464,7 +464,7 @@ impl VisChild {
 trait Variable {
     fn decode<'a>(
         &'a self,
-        data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
+        data: &DataSources,
         series: &mut BTreeMap<&'a str, Series>,
         warn: &mut dyn FnMut(LegacyXmlWarning),
     ) -> bool;
@@ -513,7 +513,7 @@ impl SourceVariable {
 impl Variable for SourceVariable {
     fn decode<'a>(
         &'a self,
-        data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
+        data: &DataSources,
         series: &mut BTreeMap<&'a str, Series>,
         _warn: &mut dyn FnMut(LegacyXmlWarning),
     ) -> bool {
@@ -527,10 +527,11 @@ impl Variable for SourceVariable {
             None
         };
 
-        let (categories, mut data) = if let Some(source) = data.get(&self.source)
-            && let Some(values) = source.get(&self.variable)
+        let (categories, mut data) = if let Some(source) = data.0.get(&self.source)
+            && let Some(values) = source.0.get(&self.variable)
         {
             let categories = values
+                .data
                 .iter()
                 .map(|value| {
                     value
@@ -539,7 +540,7 @@ impl Variable for SourceVariable {
                         .and_then(|v| (v >= 0.0 && v < usize::MAX as f64).then_some(v as usize))
                 })
                 .collect();
-            (categories, values.clone())
+            (categories, values.data.clone())
         } else {
             (Vec::new(), Vec::new())
         };
@@ -617,7 +618,7 @@ impl DerivedVariable {
 impl Variable for DerivedVariable {
     fn decode<'a>(
         &'a self,
-        _data: &IndexMap<String, IndexMap<String, Vec<Datum<String>>>>,
+        _data: &DataSources,
         series: &mut BTreeMap<&'a str, Series>,
         warn: &mut dyn FnMut(LegacyXmlWarning),
     ) -> bool {
@@ -1380,13 +1381,13 @@ impl Graph {
 
         let mut data = HashMap::new();
         let mut coords = Vec::with_capacity(dims.len());
-        let cell_formats = self.interval.labeling.decode_format_map(&series);
+        let cell_formats = self.interval.labeling.decode_format_map(series);
         let cell_footnotes = series.get("footnotes");
         'outer: for (i, cell) in cell.values.iter().enumerate() {
             coords.clear();
             for dim in dims {
                 if let Some(Some(coordinate)) = dim.series.categories.get(i)
-                    && let Some(locator) = dim.series.coordinate_to_index.borrow().get(&coordinate)
+                    && let Some(locator) = dim.series.coordinate_to_index.borrow().get(coordinate)
                     && let Some(index) = locator.as_leaf()
                 {
                     coords.push(index);
@@ -1407,15 +1408,14 @@ impl Graph {
 
             if let Some(cell_footnotes) = &cell_footnotes
                 && let Some(dv) = cell_footnotes.values.get(i)
+                && let Some(s) = dv.as_string()
             {
-                if let Some(s) = dv.as_string() {
-                    for part in s.split(',') {
-                        if let Ok(index) = part.parse::<usize>()
-                            && let Some(index) = index.checked_sub(1)
-                            && let Some(footnote) = footnotes.get(index)
-                        {
-                            value.add_footnote(footnote);
-                        }
+                for part in s.split(',') {
+                    if let Ok(index) = part.parse::<usize>()
+                        && let Some(index) = index.checked_sub(1)
+                        && let Some(footnote) = footnotes.get(index)
+                    {
+                        value.add_footnote(footnote);
                     }
                 }
             }
@@ -1462,11 +1462,11 @@ impl Graph {
                         for target in &targets {
                             target.decode(
                                 intersect,
-                                &look,
-                                &series,
+                                look,
+                                series,
                                 dims,
                                 data,
-                                &footnotes,
+                                footnotes,
                                 has_cell_footnotes,
                             );
                         }
@@ -2218,7 +2218,7 @@ fn decode_dimension<'a>(
         && let Some(label) = &axis.label
     {
         let mut dimension_style = AreaStyle::default_for_area(Area::Labels(a));
-        let style = label.style.get(&styles);
+        let style = label.style.get(styles);
         let fg = style;
         let bg = label.text_frame_style.as_ref().and_then(|r| r.get(styles));
         (
@@ -2243,7 +2243,7 @@ fn decode_dimension<'a>(
     };
 
     let variables = variables
-        .into_iter()
+        .iter()
         .map(|(series, _level)| *series)
         .collect::<Vec<_>>();
 
@@ -2382,7 +2382,7 @@ fn decode_axis_dimensions<'a, 'b>(
         }
     }
     if !dim_vars.is_empty() {
-        categorical_vars.push(&dim_vars[0].0);
+        categorical_vars.push(dim_vars[0].0);
         decode_dimension(&dim_vars, axes, styles, a, footnotes, dims);
     }
     categorical_vars
index 24554b6f7b989bc334a1f4daf8835098a06f931c..849123520e80a8db218443a3a0ffdb4084027458 100644 (file)
@@ -1,4 +1,5 @@
 use std::{
+    borrow::Cow,
     fmt::Debug,
     io::{Read, Seek},
     iter::repeat,
@@ -241,7 +242,7 @@ impl LightTable {
         let layer = structure
             .axis_dimensions(Axis3::Z)
             .map(|d| {
-                if d.len() == 0 {
+                if d.is_empty() {
                     0
                 } else {
                     let category = remainder % d.len();
@@ -276,16 +277,16 @@ impl LightTable {
                 }
             })
             .collect::<Vec<_>>();
-        let pivot_table = PivotTable::new(structure)
+        PivotTable::new(structure)
             .with_style(PivotTableStyle {
                 look: Arc::new(self.decode_look(encoding, warn)),
                 rotate_inner_column_labels: self.header.rotate_inner_column_labels,
                 rotate_outer_row_labels: self.header.rotate_outer_row_labels,
                 show_grid_lines: self.borders.show_grid_lines,
-                show_title: n1.map_or(true, |x1| x1.show_title != 10),
-                show_caption: n1.map_or(true, |x1| x1.show_caption),
-                show_values: n1.map_or(None, |x1| x1.show_values.decode(&mut warn)),
-                show_variables: n1.map_or(None, |x1| x1.show_variables.decode(&mut warn)),
+                show_title: n1.is_none_or(|x1| x1.show_title != 10),
+                show_caption: n1.is_none_or(|x1| x1.show_caption),
+                show_values: n1.and_then(|x1| x1.show_values.decode(&mut warn)),
+                show_variables: n1.and_then(|x1| x1.show_variables.decode(&mut warn)),
                 sizing: self.table_settings.sizing.decode(
                     &self.formats.column_widths,
                     n2.map_or(&[], |x2| &x2.row_heights),
@@ -344,8 +345,7 @@ impl LightTable {
             })
             .with_footnotes(footnotes)
             .with_data(cells)
-            .with_layer(&layer);
-        pivot_table
+            .with_layer(&layer)
     }
 }
 
@@ -390,7 +390,7 @@ enum Version {
 #[derive(Debug)]
 struct Titles {
     #[br(args(context))]
-    title: Value,
+    _title: Value,
     #[br(temp)]
     _1: Optional<One>,
     #[br(args(context))]
@@ -419,7 +419,7 @@ struct One;
 struct Zero;
 
 #[binrw::parser(reader, endian)]
-pub fn parse_explicit_optional<'a, T, A>(args: A, ...) -> BinResult<Option<T>>
+pub fn parse_explicit_optional<'a, T, A>(args: A, _: ...) -> BinResult<Option<T>>
 where
     T: BinRead<Args<'a> = A>,
 {
@@ -434,7 +434,7 @@ where
 }
 
 #[binrw::parser(reader, endian)]
-pub(super) fn parse_vec<'a, T, A>(inner: A, ...) -> BinResult<Vec<T>>
+pub(super) fn parse_vec<'a, T, A>(inner: A, _: ...) -> BinResult<Vec<T>>
 where
     T: BinRead<Args<'a> = A>,
     A: Clone,
@@ -619,7 +619,7 @@ impl Area {
                 bold: (self.style & 1) != 0,
                 italic: (self.style & 2) != 0,
                 underline: self.underline,
-                font: self.typeface.decode(encoding),
+                font: Cow::from(self.typeface.decode(encoding)),
                 fg: if data_row == RowParity::Odd && self.alternate {
                     self.alt_fg
                 } else {
@@ -803,9 +803,9 @@ struct Sizing {
     #[br(parse_with(parse_vec))]
     column_keeps: Vec<(i32, i32)>,
     #[br(parse_with(parse_vec))]
-    row_point_keeps: Vec<[i32; 3]>,
+    _row_point_keeps: Vec<[i32; 3]>,
     #[br(parse_with(parse_vec))]
-    column_point_keeps: Vec<[i32; 3]>,
+    _column_point_keeps: Vec<[i32; 3]>,
 }
 
 impl Sizing {
@@ -824,9 +824,9 @@ impl Sizing {
             } else {
                 Some(Box::new(look::Sizing {
                     widths: widths.into(),
-                    breaks: breaks.into_iter().map(|b| *b as usize).collect(),
+                    breaks: breaks.iter().map(|b| *b as usize).collect(),
                     keeps: keeps
-                        .into_iter()
+                        .iter()
                         .map(|(low, high)| *low as usize..*high as usize)
                         .collect(),
                 }))
@@ -917,7 +917,7 @@ where
 }
 
 #[binrw::parser(reader, endian)]
-fn parse_counted<T, A>(args: A, ...) -> BinResult<T>
+fn parse_counted<T, A>(args: A, _: ...) -> BinResult<T>
 where
     for<'a> T: BinRead<Args<'a> = A>,
     A: Clone,
@@ -973,7 +973,7 @@ struct Formats {
     #[br(parse_with(parse_vec))]
     column_widths: Vec<i32>,
     locale: U32String,
-    current_layer: i32,
+    _current_layer: i32,
     #[br(temp, parse_with(parse_bool))]
     _x7: bool,
     #[br(temp, parse_with(parse_bool))]
@@ -1027,7 +1027,7 @@ impl Formats {
 
         if let Ok(locale) = str::from_utf8(&self.locale.string)
             && let Some(dot) = locale.find('.')
-            && let Some(encoding) = Encoding::for_label(locale[dot + 1..].as_bytes())
+            && let Some(encoding) = Encoding::for_label(&locale.as_bytes()[dot + 1..])
         {
             encoding
         } else {
@@ -1089,8 +1089,8 @@ struct Y1 {
 #[br(little)]
 #[derive(Debug)]
 struct Y2 {
-    custom_currency: CustomCurrency,
-    missing: u8,
+    _custom_currency: CustomCurrency,
+    _missing: u8,
     #[br(temp, parse_with(parse_bool))]
     _x17: bool,
 }
@@ -1149,11 +1149,11 @@ struct N2 {
     #[br(parse_with(parse_vec))]
     row_heights: Vec<i32>,
     #[br(parse_with(parse_vec))]
-    style_map: Vec<(i64, i16)>,
+    _style_map: Vec<(i64, i16)>,
     #[br(parse_with(parse_vec))]
-    styles: Vec<StylePair>,
+    _styles: Vec<StylePair>,
     #[br(parse_with(parse_counted))]
-    tail: Optional<[u8; 8]>,
+    _tail: Optional<[u8; 8]>,
 }
 
 #[binread]
@@ -1168,7 +1168,7 @@ struct N3 {
     #[br(magic = 1u8, temp)]
     _one: (),
     inner: Optional<N3Inner>,
-    y2: Y2,
+    _y2: Y2,
     #[br(temp)]
     _tail: Optional<N3Tail>,
 }
@@ -1651,7 +1651,7 @@ struct ValueMods {
     #[br(parse_with(parse_vec))]
     subscripts: Vec<U32String>,
     #[br(if(context.version == Version::V1))]
-    v1: Option<ValueModsV1>,
+    _v1: Option<ValueModsV1>,
     #[br(if(context.version == Version::V3), parse_with(parse_counted))]
     v3: ValueModsV3,
 }
@@ -1699,7 +1699,7 @@ impl ValueMods {
                 bold: font_style.bold,
                 italic: font_style.italic,
                 underline: font_style.underline,
-                font: font_style.typeface.decode(encoding),
+                font: Cow::from(font_style.typeface.decode(encoding)),
                 fg: font_style.fg,
                 bg: font_style.bg,
                 size: (font_style.size as i32) * 4 / 3,
@@ -1811,7 +1811,7 @@ struct FontStyle {
     #[br(parse_with(parse_bool))]
     underline: bool,
     #[br(parse_with(parse_bool))]
-    show: bool,
+    _show: bool,
     #[br(parse_with(parse_color))]
     fg: Color,
     #[br(parse_with(parse_color))]
@@ -1923,7 +1923,7 @@ enum Child {
         #[br(temp, magic(b"\0\x01"))]
         _x23: i32,
         #[br(magic(-1i32), parse_with(parse_vec), args(context))]
-        subcategories: Vec<Box<Category>>,
+        subcategories: Vec<Category>,
     },
 }
 
index a2ad34871a4aad0eef5f5780eadb4ab04211ad7c..39f737e16f3afea108ae3ddf477e021ef1252d17 100644 (file)
@@ -8,12 +8,10 @@ use std::{
 
 use anyhow::Context;
 use binrw::{BinRead, error::ContextExt};
-use cairo::ImageSurface;
-use zip::ZipArchive;
 
 use crate::{
     output::{
-        Details, Item, ItemKind, ItemRefIterator, Itemlike, SpvInfo, SpvMembers, Text,
+        Item, ItemKind, ItemRefIterator, Itemlike, SpvGraphMembers, SpvInfo, SpvMembers, Text,
         page::PageSetup,
         pivot::{
             Length,
@@ -21,8 +19,7 @@ use crate::{
         },
     },
     spv::{
-        Error,
-        legacy_bin::LegacyBin,
+        Error, SpvArchive,
         read::{TableType, Warning, WarningDetails, legacy_xml::Visualization, light::LightTable},
     },
 };
@@ -61,7 +58,7 @@ impl StructureMember {
                 .page_setup
                 .take()
                 .map(|page_setup| page_setup.decode(warn, member_name)),
-            items: heading.decode(member_name, warn),
+            items: heading.decode(member_name),
         })
     }
 }
@@ -77,7 +74,7 @@ pub struct OutlineHeading {
 
 impl OutlineHeading {
     pub fn spv_info(&self) -> SpvInfo {
-        SpvInfo::new(&self.structure_member)
+        SpvInfo::new().with_structure_member(&self.structure_member)
     }
 }
 
@@ -88,7 +85,7 @@ pub enum OutlineItem {
 }
 
 impl OutlineItem {
-    pub fn read_item<R, F>(self, archive: &mut ZipArchive<R>, warn: &mut F) -> Item
+    pub fn read_item<R, F>(self, archive: &mut SpvArchive<R>, warn: &mut F) -> Item
     where
         R: Read + Seek,
         F: FnMut(Warning),
@@ -96,11 +93,12 @@ impl OutlineItem {
         match self {
             OutlineItem::Container(container) => {
                 let mut spv_info = container.spv_info().clone();
+                dbg!(&spv_info);
                 let result = match container.content {
                     Content::Table(table) => table.decode(archive, &mut *warn),
-                    Content::Graph(_) => Err(Error::GraphTodo),
+                    Content::Graph(graph) => graph.decode(archive).map(|graph| graph.into_item()),
                     Content::Text(container_text) => Ok(container_text.into_item()),
-                    Content::Image(image) => image.decode(archive),
+                    Content::Image(image) => image.decode(archive).map(|image| image.into_item()),
                     Content::Model => Err(Error::ModelTodo),
                     Content::Tree => Err(Error::TreeTodo),
                 };
@@ -114,6 +112,7 @@ impl OutlineItem {
                     .with_show(container.show)
                     .with_command_name(Some(container.command_name))
                     .with_label(container.label)
+                    .with_spv_info(spv_info)
             }
             OutlineItem::Heading(mut heading) => {
                 let expand = heading.expand;
@@ -185,7 +184,11 @@ impl Itemlike for OutlineItem {
         Some(Cow::Owned(spv_info))
     }
 
-    fn iter_in_order(&self) -> ItemRefIterator<'_, Self>
+    /// Constructs an iterator for [preorder traversal] of the item and its
+    /// children.
+    ///
+    /// [preorder traversal]: https://en.wikipedia.org/wiki/Tree_traversal
+    fn iter_preorder(&self) -> ItemRefIterator<'_, Self>
     where
         Self: Sized,
     {
@@ -219,8 +222,10 @@ pub struct Container {
     structure_member: String,
     show: bool,
     page_break_before: bool,
+    #[allow(dead_code)]
     text_align: Option<HorzAlign>,
     command_name: String,
+    #[allow(dead_code)]
     width: Option<Length>,
     label: String,
     content: Content,
@@ -228,7 +233,13 @@ pub struct Container {
 
 impl Container {
     pub fn spv_info(&self) -> SpvInfo {
-        SpvInfo::new(&self.structure_member).with_members(self.content.members())
+        SpvInfo::new()
+            .with_structure_member(&self.structure_member)
+            .with_members(self.content.members())
+    }
+
+    pub fn content(&self) -> &Content {
+        &self.content
     }
 }
 
@@ -276,6 +287,7 @@ impl Content {
 #[derive(Clone, Debug)]
 pub struct Table {
     subtype: String,
+    #[allow(dead_code)]
     table_type: TableType,
     look: Option<Box<Look>>,
     bin_member: String,
@@ -294,31 +306,20 @@ impl Table {
         }
     }
 
-    fn decode<R, F>(self, archive: &mut ZipArchive<R>, mut warn: F) -> Result<Item, Error>
+    fn decode<R, F>(self, archive: &mut SpvArchive<R>, mut warn: F) -> Result<Item, Error>
     where
         R: Read + Seek,
         F: FnMut(Warning),
     {
         if let Some(xml_member_name) = &self.xml_member {
-            let bin_member_name = &self.bin_member;
-            let mut bin_member = archive.by_name(bin_member_name)?;
-            let mut bin_data = Vec::with_capacity(bin_member.size() as usize);
-            bin_member.read_to_end(&mut bin_data)?;
-            let mut cursor = Cursor::new(bin_data);
-            let legacy_bin = LegacyBin::read(&mut cursor).map_err(|e| {
-                e.with_message(format!(
-                    "While parsing {bin_member_name:?} as legacy binary SPV member"
-                ))
-            })?;
-            let data = legacy_bin.decode(&mut |w| {
+            let data = archive.read_legacy_bin(&self.bin_member, &mut |warning| {
                 warn(Warning {
-                    member: bin_member_name.clone(),
-                    details: WarningDetails::LegacyBinWarning(w),
+                    member: xml_member_name.clone(),
+                    details: WarningDetails::LegacyBinWarning(warning),
                 })
-            });
-            drop(bin_member);
+            })?; // XXX improve error message
 
-            let member = BufReader::new(archive.by_name(&xml_member_name)?);
+            let member = BufReader::new(archive.0.by_name(xml_member_name)?);
             let visualization: Visualization = match serde_path_to_error::deserialize(
                 &mut quick_xml::de::Deserializer::from_reader(member),
             )
@@ -338,10 +339,7 @@ impl Table {
             Ok(pivot_table.into_item())
         } else {
             let member_name = self.bin_member.clone();
-            let mut light = archive.by_name(&member_name)?;
-            let mut data = Vec::with_capacity(light.size() as usize);
-            light.read_to_end(&mut data)?;
-            let mut cursor = Cursor::new(data);
+            let mut cursor = Cursor::new(archive.read_member(&member_name)?);
 
             let table = LightTable::read(&mut cursor).map_err(|e| {
                 e.with_message(format!(
@@ -368,13 +366,11 @@ impl Image {
     fn members(&self) -> SpvMembers {
         SpvMembers::Image(self.member.clone())
     }
-    fn decode<R>(self, archive: &mut ZipArchive<R>) -> Result<Item, Error>
+    fn decode<R>(self, archive: &mut SpvArchive<R>) -> Result<crate::output::Image, Error>
     where
         R: Read + Seek,
     {
-        let mut png = archive.by_name(&self.member)?;
-        let image = ImageSurface::create_from_png(&mut png)?;
-        Ok(Details::Image(image).into_item())
+        Ok(crate::output::Image(archive.read_member(&self.member)?))
     }
 }
 
@@ -387,11 +383,30 @@ pub struct Graph {
 
 impl Graph {
     fn members(&self) -> SpvMembers {
-        SpvMembers::Graph {
+        SpvMembers::Graph(SpvGraphMembers {
             data: self.data_member.clone(),
             xml: self.xml_member.clone(),
             csv: self.csv_member.clone(),
-        }
+        })
+    }
+
+    pub fn decode<R>(&self, archive: &mut SpvArchive<R>) -> Result<crate::output::Graph, Error>
+    where
+        R: Read + Seek,
+    {
+        let xml = archive.read_member(&self.xml_member)?;
+        let data = if let Some(bin_member_name) = &self.data_member {
+            Some(archive.read_member(bin_member_name)?)
+        } else {
+            None
+        };
+        let csv = if let Some(csv_member_name) = &self.csv_member {
+            Some(archive.read_member(csv_member_name)?)
+        } else {
+            None
+        };
+        dbg!((data.is_some(), csv.is_some()));
+        Ok(crate::output::Graph { xml, data, csv })
     }
 }
 
@@ -438,11 +453,7 @@ mod raw {
     }
 
     impl Heading {
-        pub fn decode(
-            self,
-            structure_member: &str,
-            warn: &mut dyn FnMut(Warning),
-        ) -> Vec<OutlineItem> {
+        pub fn decode(self, structure_member: &str) -> Vec<OutlineItem> {
             let mut items = Vec::new();
             for child in self.children {
                 match child {
@@ -466,7 +477,7 @@ mod raw {
                                     data_member: graph.data_member,
                                     csv_member: graph.csv_member,
                                 }),
-                                graph.command_name,
+                                graph.command_name.unwrap_or_default(),
                             ),
                             ContainerContent::Text(text) => (
                                 Content::Text(Text::new(
@@ -509,11 +520,11 @@ mod raw {
                     HeadingContent::Heading(mut heading) => {
                         items.push(OutlineItem::Heading(OutlineHeading {
                             structure_member: structure_member.into(),
-                            expand: !heading.visibility.is_some(),
+                            expand: heading.visibility.is_none(),
                             label: take(&mut heading.label.text),
                             command_name: heading.command_name.take(),
                             children: heading
-                                .decode(structure_member, warn)
+                                .decode(structure_member)
                                 .into_iter()
                                 .map(Arc::new)
                                 .collect(),
@@ -792,7 +803,7 @@ mod raw {
     #[serde(rename_all = "camelCase")]
     struct Graph {
         #[serde(rename = "@commandName")]
-        command_name: String,
+        command_name: Option<String>,
 
         /// Data.
         #[serde(rename = "dataPath")]
@@ -846,7 +857,7 @@ mod raw {
         #[serde(rename = "@subType")]
         subtype: String,
         #[serde(rename = "@tableId")]
-        table_id: Option<i64>,
+        _table_id: Option<i64>,
         #[serde(rename = "@type")]
         table_type: TableType,
         table_properties: Option<Box<TableProperties>>,
index 9487bf15829c958cac6bdee0dfa0ae22f522dcd9..30adf9e71b4af8914eaf87779b025b335e5ee774 100644 (file)
@@ -1,8 +1,4 @@
-use std::{
-    fs::File,
-    io::{BufRead, BufReader, Seek},
-    path::Path,
-};
+use std::{fs::File, io::BufReader, path::Path};
 
 use crate::{
     output::{
@@ -16,175 +12,168 @@ use crate::{
 /// Tests for compatible handling of invalid HTML entity syntax.
 #[test]
 fn text1() {
-    test_raw_spvfile("text1", None);
+    test_spvfile("text1", None);
 }
 
 /// Checks that reordering categories works properly.
 #[test]
 fn light1() {
-    test_raw_spvfile("light1", None);
+    test_spvfile("light1", None);
 }
 
 /// Checks that dimensions are ordered properly and that the title reflects the
 /// user's changes.
 #[test]
 fn light2() {
-    test_raw_spvfile("light2", None);
+    test_spvfile("light2", None);
 }
 
 /// Checks against regression for a bug reading borders.
 #[test]
 fn light3() {
-    test_raw_spvfile("light3", None);
+    test_spvfile("light3", None);
 }
 
 /// Further check for dimensions and axes are ordered properly.
 #[test]
 fn light4() {
-    test_raw_spvfile("light4", None);
+    test_spvfile("light4", None);
 }
 
 /// Test `[A:B:C]` templates.
 #[test]
 fn light5() {
-    test_raw_spvfile("light5", None);
+    test_spvfile("light5", None);
 }
 
 /// Test categories to be deleted due to negative `leaf-index`.
 #[test]
 fn light6() {
-    test_raw_spvfile("light6", None);
+    test_spvfile("light6", None);
 }
 
 /// Test current layer selection.
 #[test]
 fn light7() {
-    test_raw_spvfile("light7", None);
+    test_spvfile("light7", None);
 }
 
 /// Test a different current layer selection.
 #[test]
 fn light8() {
-    test_raw_spvfile("light8", None);
+    test_spvfile("light8", None);
 }
 
 #[test]
 fn legacy1() {
-    test_raw_spvfile("legacy1", None);
+    test_spvfile("legacy1", None);
 }
 
 #[test]
 fn legacy2() {
-    test_raw_spvfile("legacy2", None);
+    test_spvfile("legacy2", None);
 }
 
 #[test]
 fn legacy3() {
-    test_raw_spvfile("legacy3", None);
+    test_spvfile("legacy3", None);
 }
 
 #[test]
 fn legacy4() {
-    test_raw_spvfile("legacy4", None);
+    test_spvfile("legacy4", None);
 }
 
 /// Layers.
 #[test]
 fn legacy5() {
-    test_raw_spvfile("legacy5", None);
+    test_spvfile("legacy5", None);
 }
 
 /// Layer, with a particular layer selected.
 #[test]
 fn legacy6() {
-    test_raw_spvfile("legacy6", None);
+    test_spvfile("legacy6", None);
 }
 
 /// Regression test for `<setFormat reset="true">`.
 #[test]
 fn legacy7() {
-    test_raw_spvfile("legacy7", None);
+    test_spvfile("legacy7", None);
 }
 
 /// Checks for `Dimension::hide_all_labels`.
 #[test]
 fn legacy8() {
-    test_raw_spvfile("legacy8", None);
+    test_spvfile("legacy8", None);
 }
 
 /// Checks for caption defined as a footnote label, and for footnotes in layer
 /// values.
 #[test]
 fn legacy9() {
-    test_raw_spvfile("legacy9", None);
+    test_spvfile("legacy9", None);
 }
 
 /// Checks for footnotes in dimension labels.
 #[test]
 fn legacy10() {
-    test_raw_spvfile("legacy10", None);
+    test_spvfile("legacy10", None);
 }
 
 /// Checks for footnotes on data cells added via XML rather than `cellFootnotes`
 /// series.
 #[test]
 fn legacy11() {
-    test_raw_spvfile("legacy11", None);
+    test_spvfile("legacy11", None);
 }
 
 /// Checks that footnotes on data cells added via the `cellFootnotes` series
 /// can't be deleted via `setFormat`.
 #[test]
 fn legacy12() {
-    test_raw_spvfile("legacy12", None);
+    test_spvfile("legacy12", None);
 }
 
 /// Checks that we support multiple `<setFormat>` elements within a single
 /// `<setCellProperties>`.
 #[test]
 fn legacy13() {
-    test_raw_spvfile("legacy13", None);
+    test_spvfile("legacy13", None);
 }
 
 /// Check for correct defaults in XML looks.
 #[test]
 fn legacy14() {
-    test_raw_spvfile("legacy14", None);
+    test_spvfile("legacy14", None);
 }
 
 /// Checks that categories are ordered correctly when the first row has some
 /// missing cells (in this case, "Beta" lacks a value in the first row).
 #[test]
 fn legacy15() {
-    test_raw_spvfile("legacy15", None);
+    test_spvfile("legacy15", None);
 }
 
 /// Subscript support.
 #[test]
 fn legacy16() {
-    test_raw_spvfile("legacy16", None);
+    test_spvfile("legacy16", None);
 }
 
 /// Support styling cells with colored backgrounds.
 #[test]
 fn legacy17() {
-    test_raw_spvfile("legacy17", Some(Emphasis::Ansi));
+    test_spvfile("legacy17", Some(Emphasis::Ansi));
 }
 
-fn test_raw_spvfile(name: &str, emphasis: Option<Emphasis>) {
+fn test_spvfile(name: &str, emphasis: Option<Emphasis>) {
     let input_filename = Path::new("src/spv/testdata")
         .join(name)
         .with_extension("spv");
     let spvfile = BufReader::new(File::open(&input_filename).unwrap());
     let expected_filename = input_filename.with_extension("expected");
     let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
-    test_spvfile(spvfile, &expected, &expected_filename, emphasis);
-}
-
-fn test_spvfile<R>(spvfile: R, expected: &str, expected_filename: &Path, emphasis: Option<Emphasis>)
-where
-    R: BufRead + Seek + 'static,
-{
     let mut warnings = Vec::new();
     let result = match SpvArchive::open_reader(spvfile, None) {
         Ok(mut archive) => archive.read(|w| warnings.push(w)),
@@ -213,7 +202,7 @@ where
     renderer.render(&output, &mut actual).unwrap();
     if expected != actual {
         if std::env::var("PSPP_REFRESH_EXPECTED").is_ok() {
-            std::fs::write(expected_filename, actual).unwrap();
+            std::fs::write(&expected_filename, actual).unwrap();
             panic!("{}: refreshed output", expected_filename.display());
         } else {
             eprintln!("note: rerun with PSPP_REFRESH_EXPECTED=1 to refresh expected output");
diff --git a/rust/pspp/src/spv/testdata/.gitignore b/rust/pspp/src/spv/testdata/.gitignore
new file mode 100644 (file)
index 0000000..a59f7dc
--- /dev/null
@@ -0,0 +1 @@
+*.actual.svg
\ No newline at end of file
diff --git a/rust/pspp/src/spv/testdata/graph1.expected.svg b/rust/pspp/src/spv/testdata/graph1.expected.svg
new file mode 100644 (file)
index 0000000..b9ae21e
--- /dev/null
@@ -0,0 +1,77 @@
+<svg height="500" style="font-family: &quot;Sans Serif&quot;" viewBox="0 0 625 500" width="625" xmlns="http://www.w3.org/2000/svg" xmlns:c="http://www.example.org/">
+<text dominant-baseline="hanging" font-family="sans-serif" id="frame_subtitle_1" text-anchor="middle" x="312.5" y="28">
+Group Name
+</text>
+<text dominant-baseline="hanging" font-family="sans-serif" id="frame_title_1" text-anchor="middle" x="312.5" y="5">
+ROC Curve
+</text>
+<g id="frame_data_1">
+<rect fill="#f0f0f0" height="388" stroke="#000000" width="557.5" x="61.25" y="51"/>
+<g id="element_simpleline_1">
+<path d="M618.75,51 L590.875,51 L563,51 L563,69.47619 L535.125,69.47619 L535.125,87.95238 L507.25,87.95238 L479.375,87.95238 L479.375,106.42857 L451.5,106.42857 L423.625,106.42857 L395.75,106.42857 L367.875,106.42857 L340,106.42857 L312.125,106.42857 L312.125,124.90476 L284.25,124.90476 L284.25,143.38095 L256.375,143.38095 L228.5,143.38095 L200.625,143.38095 L200.625,161.85715 L200.625,180.33333 L200.625,198.80952 L172.75,198.80952 L172.75,217.28572 L172.75,235.7619 L172.75,254.2381 L172.75,272.7143 L172.75,291.1905 L144.875,291.1905 L144.875,309.66666 L117,309.66666 L89.125,309.66666 L89.125,328.14285 L89.125,346.61905 L89.125,365.09525 L89.125,383.57144 L89.125,402.0476 L89.125,420.5238 L89.125,439 L61.25,439" fill="none" stroke="#3e58ac"/>
+</g>
+<g id="element_simpleline_1">
+<path d="M61.25,439 L618.75,51" fill="none" stroke="#2eb848"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(61.25 51)"/>
+<g c:description="X axis facets below graph" transform="translate(61.25 439)">
+<line stroke="black" x1="0" x2="0" y1="0" y2="10"/>
+<line stroke="black" x1="111.5" x2="111.5" y1="0" y2="10"/>
+<line stroke="black" x1="223" x2="223" y1="0" y2="10"/>
+<line stroke="black" x1="334.50000000000006" x2="334.50000000000006" y1="0" y2="10"/>
+<line stroke="black" x1="446" x2="446" y1="0" y2="10"/>
+<line stroke="black" x1="557.5" x2="557.5" y1="0" y2="10"/>
+<text dominant-baseline="hanging" text-anchor="middle" x="0" y="10">
+0
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="111.5" y="10">
+0,2
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="223" y="10">
+0,4
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="334.50000000000006" y="10">
+0,6
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="446" y="10">
+0,8
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="557.5" y="10">
+1
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="278.75" y="33">
+X Axis
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 6.25 439) translate(6.25 439)">
+<text dominant-baseline="hanging" text-anchor="middle" x="194" y="0">
+Y Axis
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 0 45)" x="0" y="45">
+0
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 77.60000000000001 45)" x="77.60000000000001" y="45">
+0,2
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 155.20000000000002 45)" x="155.20000000000002" y="45">
+0,4
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 232.80000000000004 45)" x="232.80000000000004" y="45">
+0,6
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 310.40000000000003 45)" x="310.40000000000003" y="45">
+0,8
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 388 45)" x="388" y="45">
+1
+</text>
+<line stroke="black" x1="0" x2="0" y1="45" y2="55"/>
+<line stroke="black" x1="77.60000000000001" x2="77.60000000000001" y1="45" y2="55"/>
+<line stroke="black" x1="155.20000000000002" x2="155.20000000000002" y1="45" y2="55"/>
+<line stroke="black" x1="232.80000000000004" x2="232.80000000000004" y1="45" y2="55"/>
+<line stroke="black" x1="310.40000000000003" x2="310.40000000000003" y1="45" y2="55"/>
+<line stroke="black" x1="388" x2="388" y1="45" y2="55"/>
+</g>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 618.75 51) translate(618.75 51)"/>
+</g>
+</svg>
diff --git a/rust/pspp/src/spv/testdata/graph1.xml b/rust/pspp/src/spv/testdata/graph1.xml
new file mode 100644 (file)
index 0000000..1047636
--- /dev/null
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<visualization xmlns="http://xml.spss.com/visualization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creator="en" date="2020-08-27" description="ROC Curve" id="frame_graph_1" lang="tr-TR" style="frame_graph_1s" type="200000" version="1.4" xsi:schemaLocation="http://xml.spss.com/visualization http://xml.spss.com/visualization/vizml-2.9.xsd">
+  <embeddedSource id="source0">
+    <names>V10;V12;V6;V9</names>
+    <types>double;double;double;double</types>
+    <row>1;1;2,56;1</row>
+    <row>1;1;3,78;0.95</row>
+    <row>1;1;4,23;0.9</row>
+    <row>0.9523809523809523;1;4,54;0.9</row>
+    <row>0.9523809523809523;1;4,62;0.85</row>
+    <row>0.9047619047619048;1;4,93;0.85</row>
+    <row>0.9047619047619048;1;5,29;0.8</row>
+    <row>0.9047619047619048;1;5,50;0.75</row>
+    <row>0.8571428571428571;1;5,85;0.75</row>
+    <row>0.8571428571428571;1;6,10;0.7</row>
+    <row>0.8571428571428571;1;6,19;0.65</row>
+    <row>0.8571428571428571;1;6,25;0.6</row>
+    <row>0.8571428571428571;1;6,34;0.55</row>
+    <row>0.8571428571428571;1;6,42;0.5</row>
+    <row>0.8571428571428571;1;6,53;0.45</row>
+    <row>0.8095238095238095;1;6,66;0.45</row>
+    <row>0.8095238095238095;1;6,72;0.4</row>
+    <row>0.7619047619047619;1;6,77;0.4</row>
+    <row>0.7619047619047619;1;6,84;0.35</row>
+    <row>0.7619047619047619;1;7,06;0.3</row>
+    <row>0.7619047619047619;1;7,37;0.25</row>
+    <row>0.7142857142857143;1;7,49;0.25</row>
+    <row>0.6666666666666666;1;7,72;0.25</row>
+    <row>0.6190476190476191;1;8,03;0.25</row>
+    <row>0.6190476190476191;1;8,21;0.2</row>
+    <row>0.5714285714285714;1;8,39;0.2</row>
+    <row>0.5238095238095238;1;8,49;0.2</row>
+    <row>0.4761904761904762;1;8,53;0.2</row>
+    <row>0.4285714285714286;1;8,59;0.2</row>
+    <row>0.3809523809523809;1;8,72;0.2</row>
+    <row>0.3809523809523809;1;8,99;0.15</row>
+    <row>0.3333333333333333;1;9,23;0.15</row>
+    <row>0.3333333333333333;1;9,81;0.09999999999999998</row>
+    <row>0.3333333333333333;1;10,41;0.05000000000000004</row>
+    <row>0.2857142857142857;1;10,60;0.05000000000000004</row>
+    <row>0.2380952380952381;1;10,70;0.05000000000000004</row>
+    <row>0.1904761904761905;1;10,79;0.05000000000000004</row>
+    <row>0.1428571428571429;1;10,99;0.05000000000000004</row>
+    <row>0.09523809523809523;1;11,35;0.05000000000000004</row>
+    <row>0.04761904761904762;1;12,10;0.05000000000000004</row>
+    <row>0;1;13,50;0.05000000000000004</row>
+    <row>0;1;15,40;0</row>
+    <row>0;2;;0</row>
+    <row>1;2;;1</row>
+  </embeddedSource>
+  <location method="fixed" part="left" value="0px" />
+  <location method="fixed" part="right" value="625px" />
+  <location method="fixed" part="top" value="0px" />
+  <location method="fixed" part="bottom" value="500px" />
+  <extension styleCycleRepository="styleCycleRepository" subtitle="frame_subtitle_1" title1="frame_title_1" />
+  <sourceVariable categorical="true" creator="internal_aggregate" id="variable6" key="false" label="Cutoff Value" source="source0" sourceName="V6" weight="false" />
+  <sourceVariable categorical="false" creator="internal_aggregate" id="variable9" key="false" label="X Axis" source="source0" sourceName="V9" weight="false" />
+  <sourceVariable categorical="false" creator="internal_aggregate" id="variable10" key="false" label="Y Axis" source="source0" sourceName="V10" weight="false" />
+  <sourceVariable categorical="true" creator="internal_aggregate" id="variable12" key="false" label="Source of the Curve" source="source0" sourceName="V12" weight="false">
+    <format>
+      <relabel from="2" id="relabel_14" to="Reference Line" />
+    </format>
+  </sourceVariable>
+  <intervalDomain date="false" id="domain7">
+    <range exact="false" max="1" min="0" />
+  </intervalDomain>
+  <intervalDomain date="false" id="domain8">
+    <range exact="false" max="1" min="0" />
+  </intervalDomain>
+  <graph cellStyle="facetLabel_data_1s" id="frame_data_1" style="frame_data_1s">
+    <location method="fixed" part="left" value="1%" />
+    <location method="attach" part="top" target="frame_subtitle_1" />
+    <location method="fixed" part="bottom" value="99%" />
+    <location method="fixed" part="right" value="99%" />
+    <coordinates id="coordinates_45">
+      <dimension domain="domain7" id="dimension_46" lowerMargin="0%" upperMargin="0%">
+        <axis combineAxisLabels="true" id="axis_axisx_1" repeat="never" style="axis_axisx_1s">
+          <label id="label_axisx_1" style="style_52" textFrameStyle="style_50">
+            <text id="text_49">X Axis</text>
+          </label>
+          <majorTicks id="majorTicks_54" markStyle="majorTicks_axis_axisx_1markStyle" stagger="false" style="style_57" tickFrameStyle="style_55" />
+        </axis>
+      </dimension>
+      <dimension domain="domain8" id="dimension_59" lowerMargin="0%" upperMargin="0%">
+        <axis combineAxisLabels="true" id="axis_axisy_1" repeat="never" style="axis_axisy_1s">
+          <label id="label_axisy_1" style="style_65" textFrameStyle="style_63">
+            <text id="text_62">Y Axis</text>
+          </label>
+          <majorTicks id="majorTicks_67" markStyle="majorTicks_axis_axisy_1markStyle" stagger="false" style="style_70" tickFrameStyle="style_68" />
+        </axis>
+      </dimension>
+      <sameRatioTransform center="true" id="sameRatioTransform_75" />
+    </coordinates>
+    <facetLayout cellAspect="1" id="facetLayout_72" topDown="true" />
+    <path breakRepresentation="wings" dot="false" id="element_simpleline_1" labelCollisionHandling="none" name="element_simpleline_1" showCollidingLabels="true" style="element_simpleline_1s">
+      <styleBy id="styleBy13" styleCycle="styleCycleDefault" variable="variable12" />
+      <x variable="variable9" />
+      <y variable="variable10" />
+    </path>
+  </graph>
+  <labelFrame id="frame_subtitle_1" style="frame_subtitle_1s">
+    <location method="attach" part="top" target="frame_title_1" />
+    <location method="sizeToContent" part="height" />
+    <label id="label_subtitle_1" style="style_81" textFrameStyle="style_79">
+      <text>Group Name</text>
+    </label>
+  </labelFrame>
+  <labelFrame id="frame_title_1" style="frame_title_1s">
+    <location method="fixed" part="top" value="1%" />
+    <location method="sizeToContent" part="height" />
+    <label id="label_title_1" style="style_88" textFrameStyle="style_86">
+      <text>ROC Curve</text>
+    </label>
+  </labelFrame>
+  <style font-family="SansSerif" font-size="8pt;6pt;" id="c-o-n-t-a-i-n-er" pattern="0" stroke-linecap="butt" text-fit="true">
+    <style color="white" color2="transparent" id="frame_graph_1s" />
+    <style color="transparent" color2="transparent" id="frame_data_1s" />
+    <style color="#F0F0F0" color2="black" id="facetLabel_data_1s" />
+    <style color="transparent" color2="transparent" id="frame_subtitle_1s" />
+    <style id="label_subtitle_1s" />
+    <style color="black" color2="black" depth="75%" id="element_simpleline_1s" stroke-width="1pt" />
+    <style color="black" id="axis_axisx_1s" stroke-width="0.5pt" />
+    <style id="axis_axisx_1baselineStyle" />
+    <style color="black" id="majorTicks_axis_axisx_1markStyle" stroke-width="1pt" />
+    <style id="majorTicks_axis_axisx_1tickLabelStyle" />
+    <style color="black" id="axis_axisy_1s" stroke-width="0.5pt" />
+    <style id="axis_axisy_1baselineStyle" />
+    <style color="black" id="majorTicks_axis_axisy_1markStyle" stroke-width="1pt" />
+    <style id="majorTicks_axis_axisy_1tickLabelStyle" />
+    <style color="transparent" color2="transparent" id="frame_title_1s" />
+    <style id="label_title_1s" />
+    <style id="label_axisx_1s" />
+    <style id="label_axisy_1s" />
+    <style id="style_50" visible="false" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_52" margin="6px" padding="1px" />
+    <style id="style_55" visible="false" />
+    <style color="black" font-size="8pt;6pt;" id="style_57" />
+    <style id="style_63" visible="false" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_65" margin="6px" padding="1px" />
+    <style id="style_68" visible="false" />
+    <style color="black" font-size="8pt;6pt;" id="style_70" />
+    <style color="transparent" color2="transparent" id="style_79" />
+    <style color="black" color2="transparent" font-size="10pt;8pt;" font-weight="bold" id="style_81" margin="3px" padding="2px" />
+    <style color="transparent" color2="transparent" id="style_86" />
+    <style color="black" color2="transparent" font-size="12pt;8pt;" font-weight="bold" id="style_88" margin="3px" padding="2px" />
+  </style>
+  <styleCycle id="styleCycleDefault">
+    <cycle>
+      <style color="#3E58AC" />
+      <style color="#2EB848" />
+      <style color="#D3CE97" />
+      <style color="#7C287D" />
+      <style color="#FBF873" />
+      <style color="#EF3338" />
+      <style color="#48C2C5" />
+      <style color="#CCCCCC" />
+      <style color="#7AAAD5" />
+      <style color="#0A562C" />
+      <style color="#F8981D" />
+      <style color="#DDBAF1" />
+      <style color="#1A5F76" />
+      <style color="#CCFFCC" />
+      <style color="#BB3F7F" />
+      <style color="#999999" />
+      <style color="#000000" />
+      <style color="#B6E7E8" />
+      <style color="#FFFFFF" />
+      <style color="#797AA7" />
+      <style color="#70DC84" />
+      <style color="#333333" />
+      <style color="#ACD0EE" />
+      <style color="#A21619" />
+      <style color="#5D61FF" />
+      <style color="#E4E4E4" />
+      <style color="#248BAC" />
+      <style color="#B89BC9" />
+      <style color="#666666" />
+      <style color="#0D8D46" />
+    </cycle>
+  </styleCycle>
+  <styleCycle id="styleCycleRepository">
+    <cycle>
+      <style color="#3E58AC" />
+      <style color="#2EB848" />
+      <style color="#D3CE97" />
+      <style color="#7C287D" />
+      <style color="#FBF873" />
+      <style color="#EF3338" />
+      <style color="#48C2C5" />
+      <style color="#CCCCCC" />
+      <style color="#7AAAD5" />
+      <style color="#0A562C" />
+      <style color="#F8981D" />
+      <style color="#DDBAF1" />
+      <style color="#1A5F76" />
+      <style color="#CCFFCC" />
+      <style color="#BB3F7F" />
+      <style color="#999999" />
+      <style color="#000000" />
+      <style color="#B6E7E8" />
+      <style color="#FFFFFF" />
+      <style color="#797AA7" />
+      <style color="#70DC84" />
+      <style color="#333333" />
+      <style color="#ACD0EE" />
+      <style color="#A21619" />
+      <style color="#5D61FF" />
+      <style color="#E4E4E4" />
+      <style color="#248BAC" />
+      <style color="#B89BC9" />
+      <style color="#666666" />
+      <style color="#0D8D46" />
+    </cycle>
+    <cycle>
+      <style pattern="0" />
+      <style pattern="26" />
+      <style pattern="1" />
+      <style pattern="12" />
+      <style pattern="3" />
+      <style pattern="29" />
+      <style pattern="6" />
+      <style pattern="14" />
+      <style pattern="7" />
+      <style pattern="18" />
+      <style pattern="17" />
+      <style pattern="22" />
+      <style pattern="9" />
+      <style pattern="20" />
+      <style pattern="10" />
+      <style pattern="21" />
+      <style pattern="27" />
+      <style pattern="19" />
+      <style pattern="23" />
+      <style pattern="13" />
+      <style pattern="4" />
+      <style pattern="25" />
+      <style pattern="28" />
+      <style pattern="5" />
+      <style pattern="15" />
+      <style pattern="2" />
+      <style pattern="24" />
+      <style pattern="11" />
+      <style pattern="16" />
+      <style pattern="8" />
+    </cycle>
+    <cycle>
+      <style size="5pt" symbol="circle" />
+      <style size="5pt" symbol="square" />
+      <style size="5pt" symbol="cross" />
+      <style glyph-sides="3" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="plus" />
+      <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="circle" />
+      <style glyph-angle="45" size="5pt" symbol="square" />
+      <style glyph-sides="5" size="5pt" symbol="flower" />
+      <style size="5pt" symbol="bowtie" />
+      <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="square" />
+      <style glyph-angle="180" glyph-sides="3" size="5pt" symbol="polygon" />
+      <style glyph-aspect="0.5" size="5pt" symbol="circle" />
+      <style glyph-sides="5" size="5pt" symbol="polygon" />
+      <style glyph-sides="8" size="5pt" symbol="star" />
+      <style glyph-aspect="0.5" size="5pt" symbol="square" />
+      <style glyph-sides="3" size="5pt" symbol="flower" />
+      <style glyph-angle="90" size="5pt" symbol="bowtie" />
+      <style glyph-angle="90" size="5pt" symbol="line" />
+      <style glyph-angle="90" glyph-aspect="0.5" glyph-sides="4" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="line" />
+      <style glyph-angle="270" glyph-sides="3" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="arrow" />
+      <style glyph-angle="90" glyph-sides="3" size="5pt" symbol="polygon" />
+    </cycle>
+    <cycle>
+      <style />
+      <style stroke-dasharray="12pt,2pt,2pt,10pt,2pt,2pt" />
+      <style stroke-dasharray="8pt,8pt" />
+      <style stroke-dasharray="10pt,2pt,2pt,2pt" />
+      <style stroke-dasharray="3pt,6pt" />
+      <style stroke-dasharray="12pt,2pt,2pt,2pt,2pt,2pt,2pt,2pt" />
+      <style stroke-dasharray="6pt,3pt,6pt" />
+      <style stroke-dasharray="8pt,1pt,8pt,7pt,2pt,7pt" />
+      <style stroke-dasharray="6pt,2pt,3pt,5pt,2pt,2pt" />
+      <style stroke-dasharray="15pt,2pt,6pt,2pt" />
+      <style stroke-dasharray="5pt,5pt" />
+      <style stroke-dasharray="8pt,2pt,4pt,2pt" />
+      <style stroke-dasharray="3pt,2pt" />
+      <style stroke-dasharray="12pt,5pt,3pt,5pt" />
+      <style stroke-dasharray="6pt,3pt,3pt,3pt" />
+      <style stroke-dasharray="12pt,3pt" />
+    </cycle>
+  </styleCycle>
+</visualization>
diff --git a/rust/pspp/src/spv/testdata/graph2.expected.svg b/rust/pspp/src/spv/testdata/graph2.expected.svg
new file mode 100644 (file)
index 0000000..7a28ebd
--- /dev/null
@@ -0,0 +1,59 @@
+<svg height="168" style="font-family: &quot;Sans Serif&quot;" viewBox="0 0 504 168" width="504" xmlns="http://www.w3.org/2000/svg" xmlns:c="http://www.example.org/">
+<text dominant-baseline="hanging" font-family="sans-serif" id="frame_subtitle_1" text-anchor="middle" x="252" y="0">
+Title
+</text>
+<g id="graph_11559">
+<line fill="#000000" stroke="#000000" x1="10" x2="10" y1="0" y2="118.20000000000002"/>
+<line fill="#000000" stroke="#000000" x1="10" x2="167.9999935913086" y1="118.20000000000002" y2="118.20000000000002"/>
+<g id="area_11560">
+<polygon fill="#f5f5f5" points="13.949999839782716 118.20000000000002 13.949999839782716 64.48308433734941 21.849999519348138 92.5093012048193 29.749999198913574 108.8579277108434 37.64999887847898 113.52896385542171 45.54999855804444 115.86448192771086 53.44999823760986 115.86448192771086 92.949996635437 115.86448192771086 100.84999631500241 115.86448192771086 108.74999599456787 104.18689156626509 116.6499956741333 59.81204819277109 124.5499953536987 108.8579277108434 132.44999503326414 113.52896385542171 156.14999407196044 113.52896385542171 156.14999407196044 118.20000000000002" stroke="#000000"/>
+</g>
+<g id="area_11564">
+<polygon fill="rgb(0, 0, 255, 0.50)" points="92.949996635437 118.20000000000002 92.949996635437 113.47200000000001 100.84999631500241 113.47200000000001 108.74999599456787 89.83200000000001 116.6499956741333 0 124.5499953536987 99.28800000000001 132.44999503326414 108.74400000000001 156.14999407196044 108.74400000000001 156.14999407196044 118.20000000000002" stroke="#000000"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(10 0)"/>
+<g c:description="X axis facets below graph" transform="translate(10 118.20000000000002)">
+<text dominant-baseline="hanging" text-anchor="middle" x="78.9999967956543" y="10">
+V2
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 0 118.20000000000002) translate(0 118.20000000000002)"/>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 167.9999935913086 0) translate(167.9999935913086 0)"/>
+</g>
+<g id="graph_11578">
+<line fill="#000000" stroke="#000000" x1="177.9999935913086" x2="177.9999935913086" y1="0" y2="118.20000000000002"/>
+<line fill="#000000" stroke="#000000" x1="177.9999935913086" x2="335.9999871826172" y1="118.20000000000002" y2="118.20000000000002"/>
+<g id="area_11579">
+<polygon fill="#f5f5f5" points="181.94999343109086 118.20000000000002 181.94999343109086 115.16923076923078 205.64999246978746 103.04615384615386 221.44999182891803 106.0769230769231 229.34999150848404 109.10769230769233 245.1499908676146 115.16923076923078 260.9499902267452 115.16923076923078 268.8499899063112 103.04615384615386 284.6499892654417 112.13846153846156 300.4499886245723 93.95384615384617 308.3499883041384 0 324.1499876632689 90.92307692307695 324.1499876632689 118.20000000000002" stroke="#000000"/>
+</g>
+<g id="area_11583">
+<polygon fill="rgb(0, 0, 255, 0.50)" points="181.94999343109086 118.20000000000002 181.94999343109086 111.63333333333334 205.64999246978746 85.36666666666667 221.44999182891803 91.93333333333335 229.34999150848404 98.50000000000001 245.1499908676146 111.63333333333334 260.9499902267452 111.63333333333334 268.8499899063112 91.93333333333335 300.4499886245723 98.50000000000001 308.3499883041384 78.80000000000003 324.1499876632689 65.66666666666669 324.1499876632689 118.20000000000002" stroke="#000000"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(177.9999935913086 0)"/>
+<g c:description="X axis facets below graph" transform="translate(177.9999935913086 118.20000000000002)">
+<text dominant-baseline="hanging" text-anchor="middle" x="78.9999967956543" y="10">
+V1
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 167.9999935913086 118.20000000000002) translate(167.9999935913086 118.20000000000002)"/>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 335.9999871826172 0) translate(335.9999871826172 0)"/>
+</g>
+<g id="graph_11597">
+<line fill="#000000" stroke="#000000" x1="345.9999871826172" x2="345.9999871826172" y1="0" y2="118.20000000000002"/>
+<line fill="#000000" stroke="#000000" x1="345.9999871826172" x2="504" y1="118.20000000000002" y2="118.20000000000002"/>
+<g id="area_11598">
+<polygon fill="#f5f5f5" points="349.9499875030518 118.20000000000002 349.9499875030518 109.21723818350326 365.74998878479005 113.70861909175164 373.64998942565916 82.26895273401298 381.5499900665283 41.846524559777585 389.4499907073975 28.372381835032435 397.3499913482666 59.81204819277109 405.24999198913576 73.28619091751622 413.1499926300049 82.26895273401298 421.04999327087404 113.70861909175164 436.8499945526123 113.70861909175164 460.5499964752197 113.70861909175164 484.24999839782714 113.70861909175164 484.24999839782714 118.20000000000002" stroke="#000000"/>
+</g>
+<g id="area_11602">
+<polygon fill="rgb(0, 0, 255, 0.50)" points="373.64998942565916 118.20000000000002 373.64998942565916 63.646153846153865 381.5499900665283 0 389.4499907073975 18.184615384615377 397.3499913482666 54.55384615384615 405.24999198913576 100.01538461538463 460.5499964752197 109.10769230769233 484.24999839782714 109.10769230769233 484.24999839782714 118.20000000000002" stroke="#000000"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(345.9999871826172 0)"/>
+<g c:description="X axis facets below graph" transform="translate(345.9999871826172 118.20000000000002)">
+<text dominant-baseline="hanging" text-anchor="middle" x="79.0000064086914" y="10">
+V3
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 335.9999871826172 118.20000000000002) translate(335.9999871826172 118.20000000000002)"/>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 504 0) translate(504 0)"/>
+</g>
+</svg>
diff --git a/rust/pspp/src/spv/testdata/graph2.xml b/rust/pspp/src/spv/testdata/graph2.xml
new file mode 100644 (file)
index 0000000..f49176c
--- /dev/null
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<visualization xmlns="http://www.ibm.com/software/analytics/spss/xml/visualization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" date="2017-11-20" id="visualization_11538" version="3.0" xsi:schemaLocation="http://www.ibm.com/software/analytics/spss/xml/visualization http://www.ibm.com/software/analytics/spss/xml/visualization/vizml-3.0.xsd">
+  <!--  Extensions  -->
+  <extension dataSetHandle_6="*" dataSetName_6="graphdataset" id="extension_1092662" styleCycleRepository="styleCycleRepository" subtitle="frame_subtitle_1" />
+  <!--  Size  -->
+  <location id="location_11539" method="fixed" part="width" value="5.25in" />
+  <location id="location_11540" method="fixed" part="height" value="1.75in" />
+  <!--  Data  -->
+  <embeddedSource id="graphdataset">
+    <names>$CASENUM;V1;V2;V3</names>
+    <types>double;double;double;double</types>
+    <row>1;0.584;15.117;43.46187179</row>
+    <row>2;0.583;14.48;31.73287179</row>
+    <row>3;0.586;13.36;31.52571795</row>
+    <row>4;0.586;14.722;31.20892308</row>
+    <row>5;0.576;12.6;31.46597436</row>
+    <row>6;0.576;14.335;31.05902564</row>
+    <row>7;0.579;14.46;30.89517949</row>
+    <row>8;0.585;18.698;39.01482051</row>
+    <row>9;0.584;14.227;32.07461538</row>
+    <row>10;0.584;14.48;31.26994872</row>
+    <row>11;0.583;14.747;31.41697436</row>
+    <row>12;0.576;14.683;32.45282051</row>
+    <row>13;0.577;14.38;31.72417949</row>
+    <row>14;0.578;15.135;31.59341026</row>
+    <row>15;0.577;14.793;31.62033333</row>
+    <row>16;0.574;14.742;31.75494872</row>
+    <row>17;0.576;17.538;32.58423077</row>
+    <row>18;0.577;17.538;32.58423077</row>
+    <row>19;0.586;14.462;32.98046154</row>
+    <row>20;0.583;15.8;32.63717949</row>
+    <row>21;0.578;14.698;31.70520513</row>
+    <row>22;0.581;14.937;31.15692308</row>
+    <row>23;0.576;14.547;32.40833333</row>
+    <row>24;0.585;14.35;32.42276923</row>
+    <row>25;0.58;14.698;33.14453846</row>
+    <row>26;0.581;14.013;31.43046154</row>
+    <row>27;0.581;14.643;33.19674359</row>
+    <row>28;0.586;14.552;31.98923077</row>
+    <row>29;0.586;14.47;33.06197436</row>
+    <row>30;0.581;14.417;32.47120513</row>
+    <row>31;0.585;14.197;33.19282051</row>
+    <row>32;0.577;15.185;32.74828205</row>
+    <row>33;0.578;14.642;32.398</row>
+    <row>34;0.585;14.863;33.41071795</row>
+    <row>35;0.585;14.962;33.65930769</row>
+    <row>36;0.585;14.93;32.6375641</row>
+    <row>37;0.585;14.875;32.05174359</row>
+    <row>38;0.585;14.867;32.38941026</row>
+    <row>39;0.586;14.867;31.90435897</row>
+    <row>40;0.584;14.825;41.74879487</row>
+    <row>41;0.584;16.138;34.14179487</row>
+    <row>42;0.584;15.208;34.20110256</row>
+  </embeddedSource>
+  <embeddedSource id="delimitedFileSource_11543">
+    <names>V3;V1;V4;V2</names>
+    <types>double;double;double;double</types>
+    <row>43.46187179;0.584;1;15.117</row>
+    <row>31.73287179;0.583;1;14.48</row>
+    <row>31.52571795;0.586;1;13.36</row>
+    <row>31.20892308;0.586;1;14.722</row>
+    <row>31.46597436;0.576;1;12.6</row>
+    <row>31.05902564;0.576;1;14.335</row>
+    <row>30.89517949;0.579;1;14.46</row>
+    <row>39.01482051;0.585;1;18.698</row>
+    <row>32.07461538;0.584;1;14.227</row>
+    <row>31.26994872;0.584;1;14.48</row>
+    <row>31.41697436;0.583;1;14.747</row>
+    <row>32.45282051;0.576;1;14.683</row>
+    <row>31.72417949;0.577;1;14.38</row>
+    <row>31.59341026;0.578;1;15.135</row>
+    <row>31.62033333;0.577;1;14.793</row>
+    <row>31.75494872;0.574;1;14.742</row>
+    <row>32.58423077;0.576;1;17.538</row>
+    <row>32.58423077;0.577;1;17.538</row>
+    <row>32.98046154;0.586;1;14.462</row>
+    <row>32.63717949;0.583;1;15.8</row>
+    <row>31.70520513;0.578;1;14.698</row>
+    <row>31.15692308;0.581;1;14.937</row>
+    <row>32.40833333;0.576;1;14.547</row>
+    <row>32.42276923;0.585;1;14.35</row>
+    <row>33.14453846;0.58;1;14.698</row>
+    <row>31.43046154;0.581;1;14.013</row>
+    <row>33.19674359;0.581;1;14.643</row>
+    <row>31.98923077;0.586;1;14.552</row>
+    <row>33.06197436;0.586;1;14.47</row>
+    <row>32.47120513;0.581;1;14.417</row>
+    <row>33.19282051;0.585;1;14.197</row>
+    <row>32.74828205;0.577;1;15.185</row>
+    <row>32.398;0.578;1;14.642</row>
+    <row>33.41071795;0.585;1;14.863</row>
+    <row>33.65930769;0.585;1;14.962</row>
+    <row>32.6375641;0.585;1;14.93</row>
+    <row>32.05174359;0.585;1;14.875</row>
+    <row>32.38941026;0.585;1;14.867</row>
+    <row>31.90435897;0.586;1;14.867</row>
+    <row>41.74879487;0.584;1;14.825</row>
+    <row>34.14179487;0.584;1;16.138</row>
+    <row>34.20110256;0.584;1;15.208</row>
+    <row>30.03162162;0.584;2;8.7</row>
+    <row>29.02621622;0.583;2;7.01</row>
+    <row>32.54816216;0.584;2;9.842</row>
+    <row>32.65248649;0.584;2;9.412</row>
+    <row>33.23437838;0.581;2;6.835</row>
+    <row>28.42156757;0.584;2;6.575</row>
+    <row>36.70716216;0.584;2;7.197</row>
+    <row>33.23162162;0.584;2;6.685</row>
+    <row>31.71156757;0.584;2;7.02</row>
+    <row>32.30372973;0.583;2;7.51</row>
+    <row>32.34667568;0.584;2;7.232</row>
+    <row>32.62878378;0.584;2;7.36</row>
+    <row>34.03805405;0.584;2;6.477</row>
+    <row>34.50802703;0.584;2;7.75</row>
+    <row>31.75762162;0.584;2;6.693</row>
+    <row>34.21737838;0.584;2;8.278</row>
+    <row>31.87459459;0.584;2;6.542</row>
+    <row>32.11878378;0.582;2;6.803</row>
+    <row>32.29256757;0.584;2;6.928</row>
+    <row>32.48716216;0.584;2;6.873</row>
+    <row>34.17724324;0.584;2;6.963</row>
+    <row>34.33089189;0.583;2;6.747</row>
+    <row>33.56810811;0.584;2;7.717</row>
+    <row>34.6182973;0.582;2;7.948</row>
+    <row>32.55154054;0.584;2;7.345</row>
+    <row>31.38635135;0.583;2;6.845</row>
+    <row>33.80589189;0.584;2;6.547</row>
+    <row>35.77635135;0.585;2;7.717</row>
+    <row>33.5212973;0.584;2;6.43</row>
+    <row>34.88167568;0.584;2;6.978</row>
+    <row>31.21621622;0.584;2;6.698</row>
+    <row>34.79491892;0.583;2;7.073</row>
+    <row>33.29410811;0.584;2;7.29</row>
+    <row>34.974;0.584;2;7.3</row>
+    <row>32.54008108;0.584;2;7.042</row>
+    <row>33.984;0.584;2;7.095</row>
+    <row>34.52194595;0.584;2;7</row>
+    <row>34.7067027;0.584;2;7.153</row>
+    <row>34.77194595;0.584;2;6.528</row>
+    <row>33.29027027;0.584;2;6.977</row>
+    <row>34.15602703;0.584;2;6.88</row>
+    <row>33.85451351;0.584;2;7.258</row>
+  </embeddedSource>
+  <sourceVariable categorical="false" id="V2" source="graphdataset" sourceName="V2" />
+  <sourceVariable categorical="false" id="V27552" source="delimitedFileSource_11543" sourceName="V2" />
+  <sourceVariable categorical="false" id="V1" source="graphdataset" sourceName="V1" />
+  <sourceVariable categorical="false" id="V13652" source="delimitedFileSource_11543" sourceName="V1" />
+  <sourceVariable categorical="false" id="V3" source="graphdataset" sourceName="V3" />
+  <sourceVariable categorical="false" id="V38328" source="delimitedFileSource_11543" sourceName="V3" />
+  <intervalDomain id="intervalDomain_11552">
+    <range exact="true" max="18.698" min="6.43" />
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_11571">
+    <range exact="true" max="0.586" min="0.574" />
+  </intervalDomain>
+  <intervalDomain id="intervalDomain_11590">
+    <range exact="true" max="43.46187179" min="28.42156757" />
+  </intervalDomain>
+  <!--  Structure  -->
+  <graph axesOutside="true" id="graph_11559">
+    <!--  Size  -->
+    <location id="location_11606" method="fixed" part="right" specifiedByUser="true" value="33.33333206176758%" />
+    <location id="location_11607" method="fixed" part="left" specifiedByUser="true" value="0%" />
+    <location id="location_11608" method="fixed" part="bottom" specifiedByUser="true" value="90%" />
+    <location id="location_11609" method="fixed" part="top" specifiedByUser="true" value="0%" />
+    <coordinates id="coordinates_11549">
+      <dimension domain="intervalDomain_11552" id="dimension_11550" niceLowerMapping="false" niceUpperMapping="false">
+        <scale id="scale_11551" method="linear" />
+        <axis id="axis_11554">
+          <label id="label_11555">
+            <text id="text_11556">V2</text>
+          </label>
+        </axis>
+      </dimension>
+      <dimension id="dimension_11557">
+        <axis id="axis_11558" />
+      </dimension>
+    </coordinates>
+    <area dot="true" id="area_11560" positionModifier="none" showDiscontinuity="false" style="style_11561" zOrder="0">
+      <binStatistic binStart="6.43" binWidth="0.6134" gridType="square" id="binStatistic_11563" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11562" method="percentCount" />
+      <x variable="V27552" />
+    </area>
+    <area dot="true" id="area_11564" positionModifier="none" showDiscontinuity="false" style="style_11565" zOrder="1">
+      <binStatistic binStart="6.43" binWidth="0.6134" gridType="square" id="binStatistic_11567" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11566" method="percentCount" />
+      <x variable="V2" />
+    </area>
+  </graph>
+  <graph axesOutside="true" id="graph_11578">
+    <!--  Size  -->
+    <location id="location_11610" method="fixed" part="right" specifiedByUser="true" value="66.66666412353516%" />
+    <location id="location_11611" method="fixed" part="left" specifiedByUser="true" value="33.33333206176758%" />
+    <location id="location_11612" method="fixed" part="bottom" specifiedByUser="true" value="90%" />
+    <location id="location_11613" method="fixed" part="top" specifiedByUser="true" value="0%" />
+    <coordinates id="coordinates_11568">
+      <dimension domain="intervalDomain_11571" id="dimension_11569" niceLowerMapping="false" niceUpperMapping="false">
+        <scale id="scale_11570" method="linear" />
+        <axis id="axis_11573">
+          <label id="label_11574">
+            <text id="text_11575">V1</text>
+          </label>
+        </axis>
+      </dimension>
+      <dimension id="dimension_11576">
+        <axis id="axis_11577" />
+      </dimension>
+    </coordinates>
+    <area dot="true" id="area_11579" positionModifier="none" showDiscontinuity="false" style="style_11580" zOrder="2">
+      <binStatistic binStart="0.574" binWidth="0.0006" gridType="square" id="binStatistic_11582" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11581" method="percentCount" />
+      <x variable="V13652" />
+    </area>
+    <area dot="true" id="area_11583" positionModifier="none" showDiscontinuity="false" style="style_11584" zOrder="3">
+      <binStatistic binStart="0.574" binWidth="0.0006" gridType="square" id="binStatistic_11586" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11585" method="percentCount" />
+      <x variable="V1" />
+    </area>
+  </graph>
+  <graph axesOutside="true" id="graph_11597">
+    <!--  Size  -->
+    <location id="location_11614" method="fixed" part="right" specifiedByUser="true" value="100%" />
+    <location id="location_11615" method="fixed" part="left" specifiedByUser="true" value="66.66666412353516%" />
+    <location id="location_11616" method="fixed" part="bottom" specifiedByUser="true" value="90%" />
+    <location id="location_11617" method="fixed" part="top" specifiedByUser="true" value="0%" />
+    <coordinates id="coordinates_11587">
+      <dimension domain="intervalDomain_11590" id="dimension_11588" niceLowerMapping="false" niceUpperMapping="false">
+        <scale id="scale_11589" method="linear" />
+        <axis id="axis_11592">
+          <label id="label_11593">
+            <text id="text_11594">V3</text>
+          </label>
+        </axis>
+      </dimension>
+      <dimension id="dimension_11595">
+        <axis id="axis_11596" />
+      </dimension>
+    </coordinates>
+    <area dot="true" id="area_11598" positionModifier="none" showDiscontinuity="false" style="style_11599" zOrder="4">
+      <binStatistic binStart="28.42156757" binWidth="0.752015211" gridType="square" id="binStatistic_11601" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11600" method="percentCount" />
+      <x variable="V38328" />
+    </area>
+    <area dot="true" id="area_11602" positionModifier="none" showDiscontinuity="false" style="style_11603" zOrder="5">
+      <binStatistic binStart="28.42156757" binWidth="0.752015211" gridType="square" id="binStatistic_11605" />
+      <summaryStatistic convertIntervalToSingleValue="true" id="summaryStatistic_11604" method="percentCount" />
+      <x variable="V3" />
+    </area>
+  </graph>
+  <labelFrame id="frame_subtitle_1" style="frame_subtitle_1s">
+    <!--  Size  -->
+    <location id="location_1092735" method="fixed" part="left" value="0%" />
+    <location id="location_1092736" method="fixed" part="right" value="100%" />
+    <location id="location_1092737" method="fixed" part="top" value="0%" />
+    <location id="location_1092738" method="fixed" part="bottom" value="0.2in" />
+    <label id="label_1092739" style="label_subtitle_1s" textFrameStyle="style1">
+      <text id="text_1092740">Title</text>
+    </label>
+  </labelFrame>
+  <!--  Styles  -->
+  <style id="c-o-n-t-a-i-n-er">
+    <style color="whitesmoke" id="style_11561" pattern="0" />
+    <style color="blue" id="style_11565" opacity="0.5" pattern="0" />
+    <style color="whitesmoke" id="style_11580" pattern="0" />
+    <style color="blue" id="style_11584" opacity="0.5" pattern="0" />
+    <style color="whitesmoke" id="style_11599" pattern="0" />
+    <style color="blue" id="style_11603" opacity="0.5" pattern="0" />
+    <style color="whitesmoke" color2="transparent" id="frame_subtitle_1s" opacity="0.20" />
+    <style color="black" font-weight="bold" id="label_subtitle_1s" />
+    <style color="transparent" color2="transparent" id="style1" visible="false" />
+  </style>
+  <!--  Style Cycles  -->
+  <styleCycle id="styleCycleRepository">
+    <cycle id="cycle_1092752">
+      <style id="style_1092753" />
+    </cycle>
+  </styleCycle>
+</visualization>
diff --git a/rust/pspp/src/spv/testdata/graph3.expected.svg b/rust/pspp/src/spv/testdata/graph3.expected.svg
new file mode 100644 (file)
index 0000000..aac1b3e
--- /dev/null
@@ -0,0 +1,86 @@
+<svg height="500" style="font-family: &quot;Sans Serif&quot;" viewBox="0 0 625 500" width="625" xmlns="http://www.w3.org/2000/svg" xmlns:c="http://www.example.org/">
+<g id="frame_data_1">
+<rect fill="#f0f0f0" height="214.5" stroke="#000000" width="489.5" x="83.25" y="5"/>
+<g id="element_bar_1">
+<rect fill="url(#pattern12_d3ce97_000000)" height="92.56696428571429" stroke="#000000" width="183.5625" x="113.84375" y="126.93303571428571"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="204.2857142857143" stroke="#000000" width="183.5625" x="358.59375" y="15.214285714285701"/>
+</g>
+<rect fill="#f0f0f0" height="214.5" stroke="#000000" width="489.5" x="83.25" y="224.5"/>
+<g id="element_bar_1">
+<rect fill="url(#pattern12_d3ce97_000000)" height="121.29464285714289" stroke="#000000" width="183.5625" x="113.84375" y="317.7053571428571"/>
+<rect fill="url(#pattern12_d3ce97_000000)" height="127.67857142857144" stroke="#000000" width="183.5625" x="358.59375" y="311.32142857142856"/>
+</g>
+<defs>
+<pattern fill="#d3ce97" height="8" id="pattern12_d3ce97_000000" patternUnits="userSpaceOnUse" width="8">
+<rect fill="#000000" height="8" width="8" x="0" y="0"/>
+
+      <rect x="0" y="0" width="1" height="4"/>
+      <rect x="5" y="0" width="3" height="4"/>
+      <rect x="1" y="4" width="4" height="4"/>
+    
+</pattern>
+</defs>
+<g c:description="X axis facets above graph" transform="translate(83.25 5)"/>
+<g c:description="X axis facets below graph" transform="translate(83.25 439)">
+<line stroke="black" x1="122.375" x2="122.375" y1="0" y2="10"/>
+<line stroke="black" x1="367.125" x2="367.125" y1="0" y2="10"/>
+<text dominant-baseline="hanging" text-anchor="middle" x="122.375" y="10">
+Value0
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="367.125" y="10">
+Value1
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="244.75" y="33">
+V0
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 6.25 439) translate(6.25 439)">
+<text dominant-baseline="hanging" text-anchor="middle" x="217" y="0">
+Percent
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 0 67)" x="0" y="67">
+.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 54.58258928571429 67)" x="54.58258928571429" y="67">
+10.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 109.16517857142858 67)" x="109.16517857142858" y="67">
+20.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 163.74776785714286 67)" x="163.74776785714286" y="67">
+30.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 219.5 67)" x="219.5" y="67">
+.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 274.0825892857143 67)" x="274.0825892857143" y="67">
+10.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 328.66517857142856 67)" x="328.66517857142856" y="67">
+20.0%
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 383.2477678571429 67)" x="383.2477678571429" y="67">
+30.0%
+</text>
+<line stroke="black" x1="0" x2="0" y1="67" y2="77"/>
+<line stroke="black" x1="54.58258928571429" x2="54.58258928571429" y1="67" y2="77"/>
+<line stroke="black" x1="109.16517857142858" x2="109.16517857142858" y1="67" y2="77"/>
+<line stroke="black" x1="163.74776785714286" x2="163.74776785714286" y1="67" y2="77"/>
+<line stroke="black" x1="219.5" x2="219.5" y1="67" y2="77"/>
+<line stroke="black" x1="274.0825892857143" x2="274.0825892857143" y1="67" y2="77"/>
+<line stroke="black" x1="328.66517857142856" x2="328.66517857142856" y1="67" y2="77"/>
+<line stroke="black" x1="383.2477678571429" x2="383.2477678571429" y1="67" y2="77"/>
+</g>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 618.75 5) translate(618.75 5)">
+<text dominant-baseline="hanging" text-anchor="middle" x="217" y="0">
+Graph0
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="107.25" y="23">
+no
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="326.75" y="23">
+yes
+</text>
+</g>
+</g>
+</svg>
diff --git a/rust/pspp/src/spv/testdata/graph3.xml b/rust/pspp/src/spv/testdata/graph3.xml
new file mode 100644 (file)
index 0000000..52513b9
--- /dev/null
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8"?><visualization xmlns="http://xml.spss.com/visualization" creator="en" date="2016-02-13" description="Bar of pct by V1" id="frame_graph_1" lang="en-US" style="frame_graph_1s" type="190001" version="1.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml.spss.com/visualization http://xml.spss.com/visualization/vizml-2.8.xsd">
+        <!-- source: graph-5737abd03490909f8922a86e4c23042c.spv -->
+       <location method="fixed" part="left" value="0px"/>
+       <location method="fixed" part="right" value="625px"/>
+       <location method="fixed" part="top" value="0px"/>
+       <location method="fixed" part="bottom" value="500px"/>
+       <extension styleCycleRepository="styleCycleRepository"/>
+       <embeddedSource id="source0"><names>$PERCENT;V4;V8</names><types>double;double;double</types><row>16.95906432748538;0;1</row><row>22.22222222222222;1;1</row><row>37.42690058479532;0;2</row><row>23.39181286549708;1;2</row></embeddedSource>
+       <sourceVariable categorical="true" creator="external_datasource" id="variable4" key="false" label="label0" shortLabel="short0" source="source0" sourceName="V4" weight="false">
+               <format>
+                       <relabel from="0" id="relabel_7" to="no"/>
+                       <relabel from="1" id="relabel_8" to="yes"/>
+               </format>
+       </sourceVariable>
+       <sourceVariable categorical="true" creator="external_datasource" id="variable8" key="false" label="label1" shortLabel="short1" source="source0" sourceName="V8" weight="false">
+               <format>
+                       <relabel from="1" id="relabel_11" to="Value0"/>
+                       <relabel from="2" id="relabel_12" to="Value1"/>
+               </format>
+       </sourceVariable>
+       <sourceVariable categorical="false" creator="internal_aggregate" id="variable9" key="false" label="Percent" shortLabel="Percent" source="source0" sourceName="$PERCENT" weight="false">
+               <extension statistic="percentageOfCases"/>
+               <format hiddenseparator="false" minimumFractionDigits="1" minimumIntegerDigits="0" suffix="%" useGrouping="false"/>
+       </sourceVariable>
+       <intervalDomain date="false" id="domain11">
+               <range exact="false" max="0" min="0"/>
+       </intervalDomain>
+       
+       <graph cellStyle="facetLabel_data_1s" id="frame_data_1" style="frame_data_1s">
+               <location method="fixed" part="left" value="1%"/>
+               <location method="fixed" part="top" value="1%"/>
+               <location method="fixed" part="bottom" value="99%"/>
+               <location method="fixed" part="right" value="99%"/>
+               <coordinates id="coordinates_44">
+                       <dimension id="dimension_45" lowerMargin="5%" upperMargin="5%">
+                               <axis combineAxisLabels="true" id="axis_axisx_1" repeat="never" style="axis_axisx_1s">
+                                       <label id="label_axisx_1" style="style_51" textFrameStyle="style_49"><text id="text_48">V0</text></label>
+                                       <majorTicks id="majorTicks_53" markStyle="majorTicks_axis_axisx_1markStyle" stagger="false" style="style_56" tickFrameStyle="style_54"/>
+                               </axis>
+                       </dimension>
+                       <dimension domain="domain11" id="dimension_58" unionDomain="true" upperMargin="5%">
+                               <axis combineAxisLabels="true" id="axis_axisy_1" repeat="never" style="axis_axisy_1s">
+                                       <label id="label_axisy_1" style="style_64" textFrameStyle="style_62"><text id="text_61">Percent</text></label>
+                                       <majorTicks id="majorTicks_66" markStyle="majorTicks_axis_axisy_1markStyle" stagger="false" style="style_69" tickFrameStyle="style_67"/>
+                               </axis>
+                       </dimension>
+               </coordinates>
+               <faceting id="faceting_71" method="cross">
+                       <cross id="cross_72">
+                               <unity id="unity_73"/>
+                               <variableReference ref="variable4"/>
+                       </cross>
+               </faceting>
+               <facetLayout id="facetLayout_75" topDown="true">
+                       <facetLevel gap="2%" id="facetLabel_panel_1" level="1">
+                               <axis id="axis_panel_1" opposite="true" repeat="never" style="axis_panel_1s">
+                                       <label id="label_panel_1" style="style_82" textFrameStyle="style_80"><text id="text_79">Graph0</text></label>
+                                       <majorTicks id="majorTicks_84" labelAngle="90" markStyle="majorTicks_axis_panel_1markStyle" stagger="false" style="style_87" tickFrameStyle="style_85"/>
+                               </axis>
+                       </facetLevel>
+               </facetLayout>
+               <interval dot="false" id="element_bar_1" labelCollisionHandling="none" name="element_bar_1" showCollidingLabels="true" style="element_bar_1s">
+                       <x variable="variable8"/>
+                       <y variable="variable9"/>
+               </interval>
+       </graph>
+       
+       <style font-family="SansSerif" font-size="8pt;6pt;" id="c-o-n-t-a-i-n-er" pattern="0" stroke-linecap="butt" text-fit="true">
+               <style color="white" color2="transparent" id="frame_graph_1s"/>
+               <style color="transparent" color2="transparent" id="frame_data_1s"/>
+               <style color="#F0F0F0" color2="black" id="facetLabel_data_1s"/>
+               <style color="transparent" id="axis_panel_1s"/>
+               <style id="axis_panel_1baselineStyle"/>
+               <style id="label_panel_1s"/>
+               <style id="majorTicks_axis_panel_1markStyle" visible="false"/>
+               <style id="majorTicks_axis_panel_1tickLabelStyle"/>
+               <style color="#D3CE97" color2="black" depth="75%" id="element_bar_1s" pattern="12" size="75%"/>
+               <style color="black" id="axis_axisx_1s" stroke-width="0.5pt"/>
+               <style id="axis_axisx_1baselineStyle"/>
+               <style color="black" id="majorTicks_axis_axisx_1markStyle" stroke-width="1pt"/>
+               <style id="majorTicks_axis_axisx_1tickLabelStyle"/>
+               <style color="black" id="axis_axisy_1s" stroke-width="0.5pt"/>
+               <style id="axis_axisy_1baselineStyle"/>
+               <style color="black" id="majorTicks_axis_axisy_1markStyle" stroke-width="1pt"/>
+               <style id="majorTicks_axis_axisy_1tickLabelStyle"/>
+               <style id="label_axisx_1s"/>
+               <style id="label_axisy_1s"/>
+               <style id="style_49" visible="false"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_51" margin="6px" padding="1px"/>
+               <style id="style_54" visible="false"/>
+               <style color="black" font-size="8pt;6pt;" id="style_56"/>
+               <style id="style_62" visible="false"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_64" margin="6px" padding="1px"/>
+               <style id="style_67" visible="false"/>
+               <style color="black" font-size="8pt;6pt;" id="style_69"/>
+               <style color="transparent" color2="transparent" id="style_80"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_82" margin="6px" padding="1px"/>
+               <style color="transparent" color2="transparent" id="style_85"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt;" font-weight="bold" id="style_87" margin="2px" padding="1px"/>
+       </style>
+       
+       <styleCycle id="styleCycleDefault">
+               <cycle id="cycle_37">
+                       <style id="style_styleCycleDefault" labelInside="preferred"/>
+               </cycle>
+       </styleCycle>
+       <styleCycle id="styleCycleRepository">
+               <cycle>
+                       <style color="#3E58AC"/>
+                       <style color="#2EB848"/>
+                       <style color="#D3CE97"/>
+                       <style color="#7C287D"/>
+                       <style color="#FBF873"/>
+                       <style color="#EF3338"/>
+                       <style color="#48C2C5"/>
+                       <style color="#CCCCCC"/>
+                       <style color="#7AAAD5"/>
+                       <style color="#0A562C"/>
+                       <style color="#F8981D"/>
+                       <style color="#DDBAF1"/>
+                       <style color="#1A5F76"/>
+                       <style color="#CCFFCC"/>
+                       <style color="#BB3F7F"/>
+                       <style color="#999999"/>
+                       <style color="#000000"/>
+                       <style color="#B6E7E8"/>
+                       <style color="#FFFFFF"/>
+                       <style color="#797AA7"/>
+                       <style color="#70DC84"/>
+                       <style color="#333333"/>
+                       <style color="#ACD0EE"/>
+                       <style color="#A21619"/>
+                       <style color="#5D61FF"/>
+                       <style color="#E4E4E4"/>
+                       <style color="#248BAC"/>
+                       <style color="#B89BC9"/>
+                       <style color="#666666"/>
+                       <style color="#0D8D46"/>
+               </cycle>
+               <cycle>
+                       <style pattern="0"/>
+                       <style pattern="26"/>
+                       <style pattern="1"/>
+                       <style pattern="12"/>
+                       <style pattern="3"/>
+                       <style pattern="29"/>
+                       <style pattern="6"/>
+                       <style pattern="14"/>
+                       <style pattern="7"/>
+                       <style pattern="18"/>
+                       <style pattern="17"/>
+                       <style pattern="22"/>
+                       <style pattern="9"/>
+                       <style pattern="20"/>
+                       <style pattern="10"/>
+                       <style pattern="21"/>
+                       <style pattern="27"/>
+                       <style pattern="19"/>
+                       <style pattern="23"/>
+                       <style pattern="13"/>
+                       <style pattern="4"/>
+                       <style pattern="25"/>
+                       <style pattern="28"/>
+                       <style pattern="5"/>
+                       <style pattern="15"/>
+                       <style pattern="2"/>
+                       <style pattern="24"/>
+                       <style pattern="11"/>
+                       <style pattern="16"/>
+                       <style pattern="8"/>
+               </cycle>
+               <cycle>
+                       <style size="5pt" symbol="circle"/>
+                       <style size="5pt" symbol="square"/>
+                       <style size="5pt" symbol="cross"/>
+                       <style glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="plus"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="circle"/>
+                       <style glyph-angle="45" size="5pt" symbol="square"/>
+                       <style glyph-sides="5" size="5pt" symbol="flower"/>
+                       <style size="5pt" symbol="bowtie"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="square"/>
+                       <style glyph-angle="180" glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style glyph-aspect="0.5" size="5pt" symbol="circle"/>
+                       <style glyph-sides="5" size="5pt" symbol="polygon"/>
+                       <style glyph-sides="8" size="5pt" symbol="star"/>
+                       <style glyph-aspect="0.5" size="5pt" symbol="square"/>
+                       <style glyph-sides="3" size="5pt" symbol="flower"/>
+                       <style glyph-angle="90" size="5pt" symbol="bowtie"/>
+                       <style glyph-angle="90" size="5pt" symbol="line"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" glyph-sides="4" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="line"/>
+                       <style glyph-angle="270" glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="arrow"/>
+                       <style glyph-angle="90" glyph-sides="3" size="5pt" symbol="polygon"/>
+               </cycle>
+               <cycle>
+                       <style/>
+                       <style stroke-dasharray="12pt,2pt,2pt,10pt,2pt,2pt"/>
+                       <style stroke-dasharray="8pt,8pt"/>
+                       <style stroke-dasharray="10pt,2pt,2pt,2pt"/>
+                       <style stroke-dasharray="3pt,6pt"/>
+                       <style stroke-dasharray="12pt,2pt,2pt,2pt,2pt,2pt,2pt,2pt"/>
+                       <style stroke-dasharray="6pt,3pt,6pt"/>
+                       <style stroke-dasharray="8pt,1pt,8pt,7pt,2pt,7pt"/>
+                       <style stroke-dasharray="6pt,2pt,3pt,5pt,2pt,2pt"/>
+                       <style stroke-dasharray="15pt,2pt,6pt,2pt"/>
+                       <style stroke-dasharray="5pt,5pt"/>
+                       <style stroke-dasharray="8pt,2pt,4pt,2pt"/>
+                       <style stroke-dasharray="3pt,2pt"/>
+                       <style stroke-dasharray="12pt,5pt,3pt,5pt"/>
+                       <style stroke-dasharray="6pt,3pt,3pt,3pt"/>
+                       <style stroke-dasharray="12pt,3pt"/>
+               </cycle>
+       </styleCycle>
+</visualization>
diff --git a/rust/pspp/src/spv/testdata/graph4.expected.svg b/rust/pspp/src/spv/testdata/graph4.expected.svg
new file mode 100644 (file)
index 0000000..7b15361
--- /dev/null
@@ -0,0 +1,29 @@
+<svg height="500" style="font-family: &quot;Sans Serif&quot;" viewBox="0 0 625 500" width="625" xmlns="http://www.w3.org/2000/svg" xmlns:c="http://www.example.org/">
+<g id="frame_pie_1">
+<rect fill="rgb(0, 0, 0, 0.00)" height="242.5" stroke="rgb(0, 0, 0, 0.00)" width="416.5" x="6.25" y="5"/>
+<g id="element_element_1">
+<rect fill="#2eb848" height="121.25" stroke="#000000" width="1" x="422.75" y="126.25"/>
+</g>
+<rect fill="rgb(0, 0, 0, 0.00)" height="242.5" stroke="rgb(0, 0, 0, 0.00)" width="416.5" x="6.25" y="252.5"/>
+<g id="element_element_1">
+<rect fill="#3e58ac" height="121.25" stroke="#000000" width="1" x="207.78225806451613" y="373.75"/>
+</g>
+<g id="element_element_1">
+<rect fill="#2eb848" height="121.25" stroke="#000000" width="1" x="154.04032258064518" y="373.75"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(6.25 5)"/>
+<g c:description="X axis facets below graph" transform="translate(6.25 495)"/>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 6.25 495) translate(6.25 495)"/>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 468.75 5) translate(468.75 5)">
+<text dominant-baseline="hanging" text-anchor="middle" x="245" y="0">
+Graph0
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="121.25" y="23">
+no
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="368.75" y="23">
+yes
+</text>
+</g>
+</g>
+</svg>
diff --git a/rust/pspp/src/spv/testdata/graph4.xml b/rust/pspp/src/spv/testdata/graph4.xml
new file mode 100644 (file)
index 0000000..b26dec0
--- /dev/null
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?><visualization xmlns="http://xml.spss.com/visualization" creator="en" date="2016-02-13" description="Pie of pct by value" id="frame_graph_1" lang="en-US" style="frame_graph_1s" type="190001" version="1.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml.spss.com/visualization http://xml.spss.com/visualization/vizml-2.8.xsd">
+        <!-- source: graph-5737abd03490909f8922a86e4c23042c.spv -->
+       <location method="fixed" part="left" value="0px"/>
+       <location method="fixed" part="right" value="625px"/>
+       <location method="fixed" part="top" value="0px"/>
+       <location method="fixed" part="bottom" value="500px"/>
+       <extension legend1="frame_legend_1" styleCycleRepository="styleCycleRepository"/>
+       <embeddedSource id="source0"><names>$PERCENT;V4;V8</names><types>double;double;double</types><row>26.31578947368421;1;1</row><row>54.3859649122807;0;2</row><row>19.29824561403509;1;2</row></embeddedSource>
+       <sourceVariable categorical="true" creator="external_datasource" id="variable4" key="false" label="label1" shortLabel="short1" source="source0" sourceName="V4" weight="false">
+               <format>
+                       <relabel from="0" id="relabel_1192" to="no"/>
+                       <relabel from="1" id="relabel_1193" to="yes"/>
+               </format>
+       </sourceVariable>
+       <sourceVariable categorical="true" creator="external_datasource" id="variable8" key="false" label="label0" shortLabel="short0" source="source0" sourceName="V8" weight="false">
+               <format>
+                       <relabel from="1" id="relabel_1196" to="no"/>
+                       <relabel from="2" id="relabel_1197" to="yes"/>
+               </format>
+       </sourceVariable>
+       <sourceVariable categorical="false" creator="internal_aggregate" id="variable9" key="false" shortLabel="Percent" source="source0" sourceName="$PERCENT" weight="false">
+               <extension statistic="percentageOfCases"/>
+       </sourceVariable>
+       <sourceVariable categorical="true" creator="internal_variable" id="variable8_attributes" key="false" label="label0" shortLabel="short0" source="source0" sourceName="V8" weight="false">
+               <format>
+                       <relabel from="1" id="relabel_1201" to="no"/>
+                       <relabel from="2" id="relabel_1202" to="yes"/>
+               </format>
+       </sourceVariable>
+       
+       <graph cellStyle="facetLabel_pie_1s" id="frame_pie_1" style="frame_pie_1s">
+               <location method="fixed" part="left" value="1%"/>
+               <location method="fixed" part="top" value="1%"/>
+               <location method="fixed" part="bottom" value="99%"/>
+               <location method="attach" part="right" target="legendContainer"/>
+               <coordinates id="coordinates_1223">
+                       <dimension id="dimension_1224" niceUpperMapping="false"/>
+                       <polarTransform id="polarTransform_1245"/>
+               </coordinates>
+               <faceting id="faceting_1225" method="nest">
+                       <cross id="cross_1226">
+                               <unity id="unity_1227"/>
+                               <variableReference ref="variable4"/>
+                       </cross>
+               </faceting>
+               <facetLayout id="facetLayout_1229" topDown="true">
+                       <facetLevel gap="2%" id="facetLabel_panel_1" level="1">
+                               <axis id="axis_panel_1" opposite="true" repeat="never" style="axis_panel_1s">
+                                       <label id="label_panel_1" style="style_1236" textFrameStyle="style_1234"><text id="text_1233">Graph0</text></label>
+                                       <majorTicks id="majorTicks_1238" labelAngle="90" markStyle="majorTicks_axis_panel_1markStyle" stagger="false" style="style_1241" tickFrameStyle="style_1239"/>
+                               </axis>
+                       </facetLevel>
+               </facetLayout>
+               <interval dot="false" id="element_element_1" labelCollisionHandling="none" name="element_element_1" positionModifier="stack" showCollidingLabels="true" style="element_element_1s">
+                       <styleBy id="styleBy11" styleCycle="styleCycleDefault" variable="variable8"/>
+                       <x variable="variable9"/>
+               </interval>
+       </graph>
+       
+       <container id="legendContainer" style="style1">
+               <location method="same" part="top" target="frame_pie_1"/>
+               <location method="sizeToContent" part="height"/>
+               <location method="fixed" part="left" value="75%"/>
+               <location method="fixed" part="right" value="99%"/>
+               <legend id="frame_legend_1" style="frame_legend_1s" tickFrameStyle="style_1249" tickTextStyle="style_1247">
+                       <location method="sizeToContent" part="height"/>
+                       <location method="sizeToContent" part="width"/>
+                       <location method="same" part="left" target="legendContainer"/>
+                       <location method="same" part="right" target="legendContainer"/>
+                       <location method="same" part="top" target="legendContainer"/>
+                       <label id="label_legend_1" style="style_1255" textFrameStyle="style_1253"><text id="text_1252">label0</text></label>
+                       <legendTarget id="legendTarget_legend_1" target="styleBy11"/>
+               </legend>
+       </container>
+       <style font-family="SansSerif" font-size="8pt;6pt;" id="c-o-n-t-a-i-n-er" pattern="0" stroke-linecap="butt" text-fit="true">
+               <style color="white" color2="transparent" id="frame_graph_1s"/>
+               <style color="transparent" color2="transparent" id="frame_pie_1s"/>
+               <style color="transparent" color2="transparent" id="facetLabel_pie_1s"/>
+               <style color="transparent" id="axis_panel_1s"/>
+               <style id="axis_panel_1baselineStyle"/>
+               <style id="label_panel_1s"/>
+               <style id="majorTicks_axis_panel_1markStyle" visible="false"/>
+               <style id="majorTicks_axis_panel_1tickLabelStyle"/>
+               <style color2="black" id="element_element_1s" stroke-linecap="round"/>
+               <style color="transparent" color2="transparent" id="frame_legend_1s"/>
+               <style id="legend_legend_1s"/>
+               <style id="label_legend_1s"/>
+               <style color="transparent" color2="transparent" id="style_1234"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_1236" margin="6px" padding="1px"/>
+               <style color="transparent" color2="transparent" id="style_1239"/>
+               <style color="black" color2="transparent" font-size="11pt;7pt;" font-weight="bold" id="style_1241" margin="2px" padding="1px"/>
+               <style color="black" color2="transparent" font-size="8pt;6pt;" font-weight="regular" id="style_1247"/>
+               <style color="transparent" color2="transparent" id="style_1249"/>
+               <style color="transparent" color2="transparent" id="style_1253"/>
+               <style color="black" color2="transparent" font-size="10pt;6pt;" font-weight="regular" id="style_1255" margin="3px" padding="2px"/>
+               <style color="transparent" color2="transparent" id="style1"/>
+       </style>
+       
+       <styleCycle id="styleCycleDefault">
+               <cycle>
+                       <style color="#3E58AC"/>
+                       <style color="#2EB848"/>
+                       <style color="#D3CE97"/>
+                       <style color="#7C287D"/>
+                       <style color="#FBF873"/>
+                       <style color="#EF3338"/>
+                       <style color="#48C2C5"/>
+                       <style color="#CCCCCC"/>
+                       <style color="#7AAAD5"/>
+                       <style color="#0A562C"/>
+                       <style color="#F8981D"/>
+                       <style color="#DDBAF1"/>
+                       <style color="#1A5F76"/>
+                       <style color="#CCFFCC"/>
+                       <style color="#BB3F7F"/>
+                       <style color="#999999"/>
+                       <style color="#000000"/>
+                       <style color="#B6E7E8"/>
+                       <style color="#FFFFFF"/>
+                       <style color="#797AA7"/>
+                       <style color="#70DC84"/>
+                       <style color="#333333"/>
+                       <style color="#ACD0EE"/>
+                       <style color="#A21619"/>
+                       <style color="#5D61FF"/>
+                       <style color="#E4E4E4"/>
+                       <style color="#248BAC"/>
+                       <style color="#B89BC9"/>
+                       <style color="#666666"/>
+                       <style color="#0D8D46"/>
+               </cycle>
+       </styleCycle>
+       <styleCycle id="styleCycleRepository">
+               <cycle>
+                       <style color="#3E58AC"/>
+                       <style color="#2EB848"/>
+                       <style color="#D3CE97"/>
+                       <style color="#7C287D"/>
+                       <style color="#FBF873"/>
+                       <style color="#EF3338"/>
+                       <style color="#48C2C5"/>
+                       <style color="#CCCCCC"/>
+                       <style color="#7AAAD5"/>
+                       <style color="#0A562C"/>
+                       <style color="#F8981D"/>
+                       <style color="#DDBAF1"/>
+                       <style color="#1A5F76"/>
+                       <style color="#CCFFCC"/>
+                       <style color="#BB3F7F"/>
+                       <style color="#999999"/>
+                       <style color="#000000"/>
+                       <style color="#B6E7E8"/>
+                       <style color="#FFFFFF"/>
+                       <style color="#797AA7"/>
+                       <style color="#70DC84"/>
+                       <style color="#333333"/>
+                       <style color="#ACD0EE"/>
+                       <style color="#A21619"/>
+                       <style color="#5D61FF"/>
+                       <style color="#E4E4E4"/>
+                       <style color="#248BAC"/>
+                       <style color="#B89BC9"/>
+                       <style color="#666666"/>
+                       <style color="#0D8D46"/>
+               </cycle>
+               <cycle>
+                       <style pattern="0"/>
+                       <style pattern="26"/>
+                       <style pattern="1"/>
+                       <style pattern="12"/>
+                       <style pattern="3"/>
+                       <style pattern="29"/>
+                       <style pattern="6"/>
+                       <style pattern="14"/>
+                       <style pattern="7"/>
+                       <style pattern="18"/>
+                       <style pattern="17"/>
+                       <style pattern="22"/>
+                       <style pattern="9"/>
+                       <style pattern="20"/>
+                       <style pattern="10"/>
+                       <style pattern="21"/>
+                       <style pattern="27"/>
+                       <style pattern="19"/>
+                       <style pattern="23"/>
+                       <style pattern="13"/>
+                       <style pattern="4"/>
+                       <style pattern="25"/>
+                       <style pattern="28"/>
+                       <style pattern="5"/>
+                       <style pattern="15"/>
+                       <style pattern="2"/>
+                       <style pattern="24"/>
+                       <style pattern="11"/>
+                       <style pattern="16"/>
+                       <style pattern="8"/>
+               </cycle>
+               <cycle>
+                       <style size="5pt" symbol="circle"/>
+                       <style size="5pt" symbol="square"/>
+                       <style size="5pt" symbol="cross"/>
+                       <style glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="plus"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="circle"/>
+                       <style glyph-angle="45" size="5pt" symbol="square"/>
+                       <style glyph-sides="5" size="5pt" symbol="flower"/>
+                       <style size="5pt" symbol="bowtie"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="square"/>
+                       <style glyph-angle="180" glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style glyph-aspect="0.5" size="5pt" symbol="circle"/>
+                       <style glyph-sides="5" size="5pt" symbol="polygon"/>
+                       <style glyph-sides="8" size="5pt" symbol="star"/>
+                       <style glyph-aspect="0.5" size="5pt" symbol="square"/>
+                       <style glyph-sides="3" size="5pt" symbol="flower"/>
+                       <style glyph-angle="90" size="5pt" symbol="bowtie"/>
+                       <style glyph-angle="90" size="5pt" symbol="line"/>
+                       <style glyph-angle="90" glyph-aspect="0.5" glyph-sides="4" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="line"/>
+                       <style glyph-angle="270" glyph-sides="3" size="5pt" symbol="polygon"/>
+                       <style size="5pt" symbol="arrow"/>
+                       <style glyph-angle="90" glyph-sides="3" size="5pt" symbol="polygon"/>
+               </cycle>
+               <cycle>
+                       <style/>
+                       <style stroke-dasharray="12pt,2pt,2pt,10pt,2pt,2pt"/>
+                       <style stroke-dasharray="8pt,8pt"/>
+                       <style stroke-dasharray="10pt,2pt,2pt,2pt"/>
+                       <style stroke-dasharray="3pt,6pt"/>
+                       <style stroke-dasharray="12pt,2pt,2pt,2pt,2pt,2pt,2pt,2pt"/>
+                       <style stroke-dasharray="6pt,3pt,6pt"/>
+                       <style stroke-dasharray="8pt,1pt,8pt,7pt,2pt,7pt"/>
+                       <style stroke-dasharray="6pt,2pt,3pt,5pt,2pt,2pt"/>
+                       <style stroke-dasharray="15pt,2pt,6pt,2pt"/>
+                       <style stroke-dasharray="5pt,5pt"/>
+                       <style stroke-dasharray="8pt,2pt,4pt,2pt"/>
+                       <style stroke-dasharray="3pt,2pt"/>
+                       <style stroke-dasharray="12pt,5pt,3pt,5pt"/>
+                       <style stroke-dasharray="6pt,3pt,3pt,3pt"/>
+                       <style stroke-dasharray="12pt,3pt"/>
+               </cycle>
+       </styleCycle>
+</visualization>
diff --git a/rust/pspp/src/spv/testdata/graph5.expected.svg b/rust/pspp/src/spv/testdata/graph5.expected.svg
new file mode 100644 (file)
index 0000000..0067381
--- /dev/null
@@ -0,0 +1,203 @@
+<svg height="500" style="font-family: &quot;Sans Serif&quot;" viewBox="0 0 625 500" width="625" xmlns="http://www.w3.org/2000/svg" xmlns:c="http://www.example.org/">
+<g id="frame_data_1">
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="48.25" y="51"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.80952380952382" stroke="#000000" width="42.858749999999986" x="91.758125" y="55.44047619047618"/>
+<rect fill="#d3ce97" height="44.40476190476191" stroke="#000000" width="42.858749999999986" x="221.633125" y="99.84523809523809"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="48.25" y="149.25"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.80952380952382" stroke="#000000" width="42.858749999999986" x="91.758125" y="153.69047619047618"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="48.25" y="247.5"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="44.404761904761926" stroke="#000000" width="42.858749999999986" x="221.633125" y="296.3452380952381"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="48.25" y="345.75"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.8095238095238" stroke="#000000" width="42.858749999999986" x="91.758125" y="350.1904761904762"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="313" y="51"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.80952380952382" stroke="#000000" width="42.858749999999986" x="356.508125" y="55.44047619047618"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="313" y="149.25"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.80952380952382" stroke="#000000" width="42.858749999999986" x="356.508125" y="153.69047619047618"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="313" y="247.5"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="88.80952380952382" stroke="#000000" width="42.858749999999986" x="356.508125" y="251.94047619047618"/>
+</g>
+<rect fill="#f0f0f0" height="93.25" stroke="#000000" width="259.75" x="313" y="345.75"/>
+<g id="element_histogram_1">
+<rect fill="#d3ce97" height="44.404761904761926" stroke="#000000" width="42.85875000000004" x="486.383125" y="394.5952380952381"/>
+</g>
+<g c:description="X axis facets above graph" transform="translate(48.25 5)">
+<text dominant-baseline="hanging" text-anchor="middle" x="262.25" y="0">
+Variable 11
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="129.875" y="23">
+V11-1
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="394.625" y="23">
+V11-2
+</text>
+</g>
+<g c:description="X axis facets below graph" transform="translate(48.25 439)">
+<line stroke="black" x1="64.9375" x2="64.9375" y1="0" y2="10"/>
+<line stroke="black" x1="194.8125" x2="194.8125" y1="0" y2="10"/>
+<line stroke="black" x1="329.6875" x2="329.6875" y1="0" y2="10"/>
+<line stroke="black" x1="459.5625" x2="459.5625" y1="0" y2="10"/>
+<text dominant-baseline="hanging" text-anchor="middle" x="64.9375" y="10">
+101.10
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="194.8125" y="10">
+102.10
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="329.6875" y="10">
+101.10
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="459.5625" y="10">
+102.10
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="262.25" y="33">
+Variable 4
+</text>
+</g>
+<g c:description="Y axis facets on left side of graph" transform="rotate(-90 6.25 439) translate(6.25 439)">
+<text dominant-baseline="hanging" text-anchor="middle" x="194" y="0">
+Frequency
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 0 32)" x="0" y="32">
+0
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 14.8015873015873 32)" x="14.8015873015873" y="32">
+1
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 29.6031746031746 32)" x="29.6031746031746" y="32">
+2
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 44.404761904761905 32)" x="44.404761904761905" y="32">
+3
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 59.2063492063492 32)" x="59.2063492063492" y="32">
+4
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 74.00793650793652 32)" x="74.00793650793652" y="32">
+5
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 88.80952380952381 32)" x="88.80952380952381" y="32">
+6
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 98.25 32)" x="98.25" y="32">
+0
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 113.0515873015873 32)" x="113.0515873015873" y="32">
+1
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 127.85317460317461 32)" x="127.85317460317461" y="32">
+2
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 142.6547619047619 32)" x="142.6547619047619" y="32">
+3
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 157.45634920634922 32)" x="157.45634920634922" y="32">
+4
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 172.2579365079365 32)" x="172.2579365079365" y="32">
+5
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 187.0595238095238 32)" x="187.0595238095238" y="32">
+6
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 196.5 32)" x="196.5" y="32">
+0
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 211.3015873015873 32)" x="211.3015873015873" y="32">
+1
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 226.1031746031746 32)" x="226.1031746031746" y="32">
+2
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 240.9047619047619 32)" x="240.9047619047619" y="32">
+3
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 255.70634920634922 32)" x="255.70634920634922" y="32">
+4
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 270.5079365079365 32)" x="270.5079365079365" y="32">
+5
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 285.3095238095238 32)" x="285.3095238095238" y="32">
+6
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 294.75 32)" x="294.75" y="32">
+0
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 309.5515873015873 32)" x="309.5515873015873" y="32">
+1
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 324.3531746031746 32)" x="324.3531746031746" y="32">
+2
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 339.1547619047619 32)" x="339.1547619047619" y="32">
+3
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 353.9563492063492 32)" x="353.9563492063492" y="32">
+4
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 368.7579365079365 32)" x="368.7579365079365" y="32">
+5
+</text>
+<text dominant-baseline="middle" text-anchor="end" transform="rotate(90 383.5595238095238 32)" x="383.5595238095238" y="32">
+6
+</text>
+<line stroke="black" x1="0" x2="0" y1="32" y2="42"/>
+<line stroke="black" x1="14.8015873015873" x2="14.8015873015873" y1="32" y2="42"/>
+<line stroke="black" x1="29.6031746031746" x2="29.6031746031746" y1="32" y2="42"/>
+<line stroke="black" x1="44.404761904761905" x2="44.404761904761905" y1="32" y2="42"/>
+<line stroke="black" x1="59.2063492063492" x2="59.2063492063492" y1="32" y2="42"/>
+<line stroke="black" x1="74.00793650793652" x2="74.00793650793652" y1="32" y2="42"/>
+<line stroke="black" x1="88.80952380952381" x2="88.80952380952381" y1="32" y2="42"/>
+<line stroke="black" x1="98.25" x2="98.25" y1="32" y2="42"/>
+<line stroke="black" x1="113.0515873015873" x2="113.0515873015873" y1="32" y2="42"/>
+<line stroke="black" x1="127.85317460317461" x2="127.85317460317461" y1="32" y2="42"/>
+<line stroke="black" x1="142.6547619047619" x2="142.6547619047619" y1="32" y2="42"/>
+<line stroke="black" x1="157.45634920634922" x2="157.45634920634922" y1="32" y2="42"/>
+<line stroke="black" x1="172.2579365079365" x2="172.2579365079365" y1="32" y2="42"/>
+<line stroke="black" x1="187.0595238095238" x2="187.0595238095238" y1="32" y2="42"/>
+<line stroke="black" x1="196.5" x2="196.5" y1="32" y2="42"/>
+<line stroke="black" x1="211.3015873015873" x2="211.3015873015873" y1="32" y2="42"/>
+<line stroke="black" x1="226.1031746031746" x2="226.1031746031746" y1="32" y2="42"/>
+<line stroke="black" x1="240.9047619047619" x2="240.9047619047619" y1="32" y2="42"/>
+<line stroke="black" x1="255.70634920634922" x2="255.70634920634922" y1="32" y2="42"/>
+<line stroke="black" x1="270.5079365079365" x2="270.5079365079365" y1="32" y2="42"/>
+<line stroke="black" x1="285.3095238095238" x2="285.3095238095238" y1="32" y2="42"/>
+<line stroke="black" x1="294.75" x2="294.75" y1="32" y2="42"/>
+<line stroke="black" x1="309.5515873015873" x2="309.5515873015873" y1="32" y2="42"/>
+<line stroke="black" x1="324.3531746031746" x2="324.3531746031746" y1="32" y2="42"/>
+<line stroke="black" x1="339.1547619047619" x2="339.1547619047619" y1="32" y2="42"/>
+<line stroke="black" x1="353.9563492063492" x2="353.9563492063492" y1="32" y2="42"/>
+<line stroke="black" x1="368.7579365079365" x2="368.7579365079365" y1="32" y2="42"/>
+<line stroke="black" x1="383.5595238095238" x2="383.5595238095238" y1="32" y2="42"/>
+</g>
+<g c:description="Y axis facets on right side of graph" transform="rotate(90 618.75 51) translate(618.75 51)">
+<text dominant-baseline="hanging" text-anchor="middle" x="194" y="0">
+Variable 12
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="46.625" y="23">
+V12-4
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="144.875" y="23">
+V12-5
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="243.125" y="23">
+V12-6
+</text>
+<text dominant-baseline="hanging" text-anchor="middle" x="341.375" y="23">
+V12-7
+</text>
+</g>
+</g>
+</svg>
diff --git a/rust/pspp/src/spv/testdata/graph5.xml b/rust/pspp/src/spv/testdata/graph5.xml
new file mode 100644 (file)
index 0000000..4f06ca6
--- /dev/null
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<visualization xmlns="http://www.ibm.com/software/analytics/spss/xml/visualization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creator="en" date="2019-05-08" description="Histogram of Kategorija" id="frame_graph_1" lang="en-US" style="frame_graph_1s" type="21000000" version="1.4" xsi:schemaLocation="http://www.ibm.com/software/analytics/spss/xml/visualization http://www.ibm.com/software/analytics/spss/xml/visualization/vizml-3.0.xsd">
+  <!-- Source: /home/blp/pspp/spv/spv/04488e072882e8f8b0c3c687e272d4af/00000000293_-4000816061397075370_chart.xml -->
+  <!--  Data  -->
+  <location method="fixed" part="left" value="0px" />
+  <location method="fixed" part="right" value="625px" />
+  <location method="fixed" part="top" value="0px" />
+  <location method="fixed" part="bottom" value="500px" />
+  <extension styleCycleRepository="styleCycleRepository" />
+  <embeddedSource id="source0">
+    <names>$WEIGHT;V11;V12;V4</names>
+    <types>double;double;double;double</types>
+    <row>5;101;4;101</row>
+    <row>4;101;5;101</row>
+    <row>2;101;7;101</row>
+    <row>1;102;4;101</row>
+    <row>7;102;5;101</row>
+    <row>4;102;6;101</row>
+    <row>5;101;4;102</row>
+    <row>1;101;6;102</row>
+    <row>10;102;7;102</row>
+  </embeddedSource>
+  <sourceVariable categorical="false" creator="external_datasource" id="variable4" key="false" shortLabel="V4" source="source0" sourceName="V4" weight="false">
+    <format hiddenseparator="false" minimumFractionDigits="2" minimumIntegerDigits="0" useGrouping="false" />
+  </sourceVariable>
+  <sourceVariable categorical="false" creator="internal_aggregate" id="variable5" key="false" label="Frequency" shortLabel="Frequency" source="source0" sourceName="$WEIGHT" weight="true" />
+  <sourceVariable categorical="true" creator="external_datasource" id="variable11" key="false" shortLabel="V11" source="source0" sourceName="V11" weight="false">
+    <format>
+      <relabel from="101" id="relabel_1270" to="V11-1" />
+      <relabel from="102" id="relabel_1271" to="V11-2" />
+    </format>
+  </sourceVariable>
+  <sourceVariable categorical="true" creator="external_datasource" id="variable12" key="false" shortLabel="V12" source="source0" sourceName="V12" weight="false" >
+    <format>
+           <relabel from="4" to="V12-4"/>
+           <relabel from="5" to="V12-5"/>
+           <relabel from="6" to="V12-6"/>
+           <relabel from="7" to="V12-7"/>
+    </format>
+  </sourceVariable>
+  <!--  Structure  -->
+  <graph cellStyle="facetLabel_data_1s" id="frame_data_1" style="frame_data_1s">
+    <location method="fixed" part="left" value="1%" />
+    <location method="fixed" part="top" value="1%" />
+    <location method="fixed" part="bottom" value="99%" />
+    <location method="fixed" part="right" value="99%" />
+    <coordinates id="coordinates_1305">
+      <dimension id="dimension_1306" lowerMargin="5%" upperMargin="5%">
+        <axis combineAxisLabels="true" id="axis_axisx_1" repeat="never" style="axis_axisx_1s">
+          <label id="label_axisx_1" style="style_1312" textFrameStyle="style_1310">
+            <text id="text_1309">Variable 4</text>
+          </label>
+          <majorTicks id="majorTicks_1314" markStyle="majorTicks_axis_axisx_1markStyle" stagger="false" style="style_1317" tickFrameStyle="style_1315" />
+        </axis>
+      </dimension>
+      <dimension id="dimension_1319" upperMargin="5%">
+        <axis combineAxisLabels="true" id="axis_axisy_1" repeat="never" style="axis_axisy_1s">
+          <label id="label_axisy_1" style="style_1325" textFrameStyle="style_1323">
+            <text id="text_1322">Frequency</text>
+          </label>
+          <majorTicks id="majorTicks_1327" markStyle="majorTicks_axis_axisy_1markStyle" stagger="false" style="style_1330" tickFrameStyle="style_1328" />
+        </axis>
+      </dimension>
+    </coordinates>
+    <faceting id="faceting_1332" method="cross">
+      <cross id="cross_1333">
+        <variableReference ref="variable11" />
+        <variableReference ref="variable12" />
+      </cross>
+    </faceting>
+    <facetLayout id="facetLayout_1336" topDown="true">
+      <facetLevel gap="2%" id="facetLabel_panel_1" level="1">
+        <axis id="axis_panel_1" opposite="true" repeat="never" style="axis_panel_1s">
+          <label id="label_panel_1" style="style_1343" textFrameStyle="style_1341">
+            <text id="text_1340">Variable 11</text>
+          </label>
+          <majorTicks id="majorTicks_1345" markStyle="majorTicks_axis_panel_1markStyle" stagger="false" style="style_1348" tickFrameStyle="style_1346" />
+        </axis>
+      </facetLevel>
+      <facetLevel gap="2%" id="facetLabel_panel_2" level="2">
+        <axis id="axis_panel_2" opposite="true" repeat="never" style="axis_panel_2s">
+          <label id="label_panel_2" style="style_1356" textFrameStyle="style_1354">
+            <text id="text_1353">Variable 12</text>
+          </label>
+          <majorTicks id="majorTicks_1358" labelAngle="90" markStyle="majorTicks_axis_panel_2markStyle" stagger="false" style="style_1361" tickFrameStyle="style_1359" />
+        </axis>
+      </facetLevel>
+    </facetLayout>
+    <interval dot="false" id="element_histogram_1" labelCollisionHandling="none" name="element_histogram_1" showCollidingLabels="true" style="element_histogram_1s">
+      <binStatistic id="binStatistic_1364" />
+      <summaryStatistic id="summaryStatistic_1365" method="count" />
+      <x variable="variable4" />
+    </interval>
+  </graph>
+  <!--  Styles  -->
+  <style font-family="SansSerif" font-size="8pt;6pt;" id="c-o-n-t-a-i-n-er" pattern="0" stroke-linecap="butt" text-fit="true">
+    <style color="white" color2="transparent" id="frame_graph_1s" />
+    <style color="transparent" color2="transparent" id="frame_data_1s" />
+    <style color="#F0F0F0" color2="black" id="facetLabel_data_1s" />
+    <style color="black" id="axis_axisx_1s" stroke-width="0.5pt" />
+    <style id="axis_axisx_1baselineStyle" />
+    <style color="black" id="majorTicks_axis_axisx_1markStyle" stroke-width="1pt" />
+    <style id="majorTicks_axis_axisx_1tickLabelStyle" />
+    <style color="black" id="axis_axisy_1s" stroke-width="0.5pt" />
+    <style id="axis_axisy_1baselineStyle" />
+    <style color="black" id="majorTicks_axis_axisy_1markStyle" stroke-width="1pt" />
+    <style id="majorTicks_axis_axisy_1tickLabelStyle" />
+    <style color="#D3CE97" color2="black" depth="75%" id="element_histogram_1s" pattern="0" width="100%" />
+    <style id="label_axisx_1s" />
+    <style id="label_axisy_1s" />
+    <style color="transparent" id="axis_panel_1s" />
+    <style id="axis_panel_1baselineStyle" />
+    <style id="label_panel_1s" />
+    <style id="majorTicks_axis_panel_1markStyle" visible="false" />
+    <style id="majorTicks_axis_panel_1tickLabelStyle" />
+    <style color="transparent" id="axis_panel_2s" />
+    <style id="axis_panel_2baselineStyle" />
+    <style id="label_panel_2s" />
+    <style id="majorTicks_axis_panel_2markStyle" visible="false" />
+    <style id="majorTicks_axis_panel_2tickLabelStyle" />
+    <style id="style_1310" visible="false" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_1312" margin="6px" padding="1px" />
+    <style id="style_1315" visible="false" />
+    <style color="black" font-size="8pt;6pt;" id="style_1317" />
+    <style id="style_1323" visible="false" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_1325" margin="6px" padding="1px" />
+    <style id="style_1328" visible="false" />
+    <style color="black" font-size="8pt;6pt;" id="style_1330" />
+    <style color="transparent" color2="transparent" id="style_1341" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_1343" margin="6px" padding="1px" />
+    <style color="transparent" color2="transparent" id="style_1346" />
+    <style color="black" color2="transparent" font-size="11pt;7pt;" font-weight="bold" id="style_1348" margin="2px" padding="1px" />
+    <style color="transparent" color2="transparent" id="style_1354" />
+    <style color="black" color2="transparent" font-size="11pt;7pt" font-weight="bold" id="style_1356" margin="6px" padding="1px" />
+    <style color="transparent" color2="transparent" id="style_1359" />
+    <style color="black" color2="transparent" font-size="11pt;7pt;" font-weight="bold" id="style_1361" margin="2px" padding="1px" />
+  </style>
+  <!--  Style Cycles  -->
+  <styleCycle id="styleCycleDefault">
+    <cycle id="cycle_1298">
+      <style id="style_styleCycleDefault" labelInside="preferred" />
+    </cycle>
+  </styleCycle>
+  <styleCycle id="styleCycleRepository">
+    <cycle>
+      <style color="#3E58AC" />
+      <style color="#2EB848" />
+      <style color="#D3CE97" />
+      <style color="#7C287D" />
+      <style color="#FBF873" />
+      <style color="#EF3338" />
+      <style color="#48C2C5" />
+      <style color="#CCCCCC" />
+      <style color="#7AAAD5" />
+      <style color="#0A562C" />
+      <style color="#F8981D" />
+      <style color="#DDBAF1" />
+      <style color="#1A5F76" />
+      <style color="#CCFFCC" />
+      <style color="#BB3F7F" />
+      <style color="#999999" />
+      <style color="#000000" />
+      <style color="#B6E7E8" />
+      <style color="#FFFFFF" />
+      <style color="#797AA7" />
+      <style color="#70DC84" />
+      <style color="#333333" />
+      <style color="#ACD0EE" />
+      <style color="#A21619" />
+      <style color="#5D61FF" />
+      <style color="#E4E4E4" />
+      <style color="#248BAC" />
+      <style color="#B89BC9" />
+      <style color="#666666" />
+      <style color="#0D8D46" />
+    </cycle>
+    <cycle>
+      <style pattern="0" />
+      <style pattern="26" />
+      <style pattern="1" />
+      <style pattern="12" />
+      <style pattern="3" />
+      <style pattern="29" />
+      <style pattern="6" />
+      <style pattern="14" />
+      <style pattern="7" />
+      <style pattern="18" />
+      <style pattern="17" />
+      <style pattern="22" />
+      <style pattern="9" />
+      <style pattern="20" />
+      <style pattern="10" />
+      <style pattern="21" />
+      <style pattern="27" />
+      <style pattern="19" />
+      <style pattern="23" />
+      <style pattern="13" />
+      <style pattern="4" />
+      <style pattern="25" />
+      <style pattern="28" />
+      <style pattern="5" />
+      <style pattern="15" />
+      <style pattern="2" />
+      <style pattern="24" />
+      <style pattern="11" />
+      <style pattern="16" />
+      <style pattern="8" />
+    </cycle>
+    <cycle>
+      <style size="5pt" symbol="circle" />
+      <style size="5pt" symbol="square" />
+      <style size="5pt" symbol="cross" />
+      <style glyph-sides="3" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="plus" />
+      <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="circle" />
+      <style glyph-angle="45" size="5pt" symbol="square" />
+      <style glyph-sides="5" size="5pt" symbol="flower" />
+      <style size="5pt" symbol="bowtie" />
+      <style glyph-angle="90" glyph-aspect="0.5" size="5pt" symbol="square" />
+      <style glyph-angle="180" glyph-sides="3" size="5pt" symbol="polygon" />
+      <style glyph-aspect="0.5" size="5pt" symbol="circle" />
+      <style glyph-sides="5" size="5pt" symbol="polygon" />
+      <style glyph-sides="8" size="5pt" symbol="star" />
+      <style glyph-aspect="0.5" size="5pt" symbol="square" />
+      <style glyph-sides="3" size="5pt" symbol="flower" />
+      <style glyph-angle="90" size="5pt" symbol="bowtie" />
+      <style glyph-angle="90" size="5pt" symbol="line" />
+      <style glyph-angle="90" glyph-aspect="0.5" glyph-sides="4" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="line" />
+      <style glyph-angle="270" glyph-sides="3" size="5pt" symbol="polygon" />
+      <style size="5pt" symbol="arrow" />
+      <style glyph-angle="90" glyph-sides="3" size="5pt" symbol="polygon" />
+    </cycle>
+    <cycle>
+      <style />
+      <style stroke-dasharray="12pt,2pt,2pt,10pt,2pt,2pt" />
+      <style stroke-dasharray="8pt,8pt" />
+      <style stroke-dasharray="10pt,2pt,2pt,2pt" />
+      <style stroke-dasharray="3pt,6pt" />
+      <style stroke-dasharray="12pt,2pt,2pt,2pt,2pt,2pt,2pt,2pt" />
+      <style stroke-dasharray="6pt,3pt,6pt" />
+      <style stroke-dasharray="8pt,1pt,8pt,7pt,2pt,7pt" />
+      <style stroke-dasharray="6pt,2pt,3pt,5pt,2pt,2pt" />
+      <style stroke-dasharray="15pt,2pt,6pt,2pt" />
+      <style stroke-dasharray="5pt,5pt" />
+      <style stroke-dasharray="8pt,2pt,4pt,2pt" />
+      <style stroke-dasharray="3pt,2pt" />
+      <style stroke-dasharray="12pt,5pt,3pt,5pt" />
+      <style stroke-dasharray="6pt,3pt,3pt,3pt" />
+      <style stroke-dasharray="12pt,3pt" />
+    </cycle>
+  </styleCycle>
+</visualization>
diff --git a/rust/pspp/src/spv/testdata/legacy-data1.bin b/rust/pspp/src/spv/testdata/legacy-data1.bin
new file mode 100644 (file)
index 0000000..f9c0599
Binary files /dev/null and b/rust/pspp/src/spv/testdata/legacy-data1.bin differ
diff --git a/rust/pspp/src/spv/testdata/legacy-data1.expected b/rust/pspp/src/spv/testdata/legacy-data1.expected
new file mode 100644 (file)
index 0000000..1736e04
--- /dev/null
@@ -0,0 +1,39 @@
+╭─────┬────────────────────────────────────────────────────────────────────────────────────────────╮
+│     │                                          tableData                                         │
+│     ├──────┬──────────┬──────────┬──────────┬──────────┬───────────┬──────────┬──────────────────┤
+│     │      │          │          │          │dimension0│           │          │                  │
+│     │      │          │dimension0│dimension0│  group0  │ dimension0│dimension1│    dimension1    │
+│Index│ cell │cellFormat│categories│  group0  │   Label  │   labels  │categories│      labels      │
+├─────┼──────┼──────────┼──────────┼──────────┼──────────┼───────────┼──────────┼──────────────────┤
+│0    │  1.00│F40.0     │       .00│      2.00│Valid     │>2 years   │       .00│Frequency         │
+│1    │   .47│F40.1     │       .00│      2.00│Valid     │>2 years   │      1.00│Percent           │
+│2    │   .47│F40.1     │       .00│      2.00│Valid     │>2 years   │      2.00│Valid Percent     │
+│3    │   .47│F40.1     │       .00│      2.00│Valid     │>2 years   │      3.00│Cumulative Percent│
+│4    │ 17.00│F40.0     │      1.00│      2.00│Valid     │2-5 years  │       .00│Frequency         │
+│5    │  7.94│F40.1     │      1.00│      2.00│Valid     │2-5 years  │      1.00│Percent           │
+│6    │  7.98│F40.1     │      1.00│      2.00│Valid     │2-5 years  │      2.00│Valid Percent     │
+│7    │  8.45│F40.1     │      1.00│      2.00│Valid     │2-5 years  │      3.00│Cumulative Percent│
+│8    │122.00│F40.0     │      2.00│      2.00│Valid     │6-10 years │       .00│Frequency         │
+│9    │ 57.01│F40.1     │      2.00│      2.00│Valid     │6-10 years │      1.00│Percent           │
+│10   │ 57.28│F40.1     │      2.00│      2.00│Valid     │6-10 years │      2.00│Valid Percent     │
+│11   │ 65.73│F40.1     │      2.00│      2.00│Valid     │6-10 years │      3.00│Cumulative Percent│
+│12   │ 45.00│F40.0     │      3.00│      2.00│Valid     │11-15 years│       .00│Frequency         │
+│13   │ 21.03│F40.1     │      3.00│      2.00│Valid     │11-15 years│      1.00│Percent           │
+│14   │ 21.13│F40.1     │      3.00│      2.00│Valid     │11-15 years│      2.00│Valid Percent     │
+│15   │ 86.85│F40.1     │      3.00│      2.00│Valid     │11-15 years│      3.00│Cumulative Percent│
+│16   │ 18.00│F40.0     │      4.00│      2.00│Valid     │16-20 years│       .00│Frequency         │
+│17   │  8.41│F40.1     │      4.00│      2.00│Valid     │16-20 years│      1.00│Percent           │
+│18   │  8.45│F40.1     │      4.00│      2.00│Valid     │16-20 years│      2.00│Valid Percent     │
+│19   │ 95.31│F40.1     │      4.00│      2.00│Valid     │16-20 years│      3.00│Cumulative Percent│
+│20   │ 10.00│F40.0     │      5.00│      2.00│Valid     │>20 years  │       .00│Frequency         │
+│21   │  4.67│F40.1     │      5.00│      2.00│Valid     │>20 years  │      1.00│Percent           │
+│22   │  4.69│F40.1     │      5.00│      2.00│Valid     │>20 years  │      2.00│Valid Percent     │
+│23   │100.00│F40.1     │      5.00│      2.00│Valid     │>20 years  │      3.00│Cumulative Percent│
+│24   │213.00│F40.0     │      6.00│      2.00│Valid     │Total      │       .00│Frequency         │
+│25   │ 99.53│F40.1     │      6.00│      2.00│Valid     │Total      │      1.00│Percent           │
+│26   │100.00│F40.1     │      6.00│      2.00│Valid     │Total      │      2.00│Valid Percent     │
+│27   │  1.00│F40.0     │      7.00│      4.00│Missing   │System     │       .00│Frequency         │
+│28   │   .47│F40.1     │      7.00│      4.00│Missing   │System     │      1.00│Percent           │
+│29   │214.00│F40.0     │      8.00│      5.00│          │Total      │       .00│Frequency         │
+│30   │100.00│F40.1     │      8.00│      5.00│          │Total      │      1.00│Percent           │
+╰─────┴──────┴──────────┴──────────┴──────────┴──────────┴───────────┴──────────┴──────────────────╯
index 4e73d09133bd72916eb5ee23ea5c0ad55575999f..e697b816ba6124948e244d1d1ea7e9a2a6bb761a 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -18,22 +18,20 @@ use std::{
     borrow::Cow,
     io::{Cursor, Seek, Write},
     iter::{repeat, repeat_n},
+    ops::Not as _,
 };
 
 use binrw::{BinWrite, Endian};
 use chrono::Utc;
 use enum_map::EnumMap;
-use quick_xml::{
-    ElementWriter, Writer as XmlWriter,
-    events::{BytesText, attributes::Attribute},
-};
+use quick_xml::{ElementWriter, Writer as XmlWriter, events::BytesText};
 use zip::{ZipWriter, write::SimpleFileOptions};
 
 use crate::{
     data::{Datum, EncodedString},
     format::{Format, Type},
     output::{
-        Details, Item, Itemlike, Text,
+        Details, Graph, Item, Itemlike, Text,
         page::{ChartSize, PageSetup},
         pivot::{
             Axis2, Axis3, Category, Dimension, Footnote, FootnoteMarkerPosition,
@@ -108,6 +106,12 @@ where
         Ok(self.writer.finish()?)
     }
 
+    fn next_table_id(&mut self) -> u64 {
+        let table_id = self.next_table_id;
+        self.next_table_id += 1;
+        table_id
+    }
+
     fn write_table<X>(
         &mut self,
         item: &Item,
@@ -117,9 +121,7 @@ where
     where
         X: Write,
     {
-        let table_id = self.next_table_id;
-        self.next_table_id += 1;
-
+        let table_id = self.next_table_id();
         let mut content = Vec::new();
         let mut cursor = Cursor::new(&mut content);
         pivot_table.write_le(&mut cursor)?;
@@ -162,6 +164,93 @@ where
         Ok(())
     }
 
+    fn write_image<X>(
+        &mut self,
+        item: &Item,
+        image: &[u8],
+        structure: &mut XmlWriter<X>,
+    ) -> Result<(), Error>
+    where
+        X: Write,
+    {
+        let table_id = self.next_table_id();
+
+        let image_name = format!("{table_id:011}_Imagegeneric.png");
+        self.writer
+            .start_file(&image_name, SimpleFileOptions::default())?;
+        self.writer.write_all(image)?;
+
+        self.container(structure, item, "vtb:object", |element| {
+            element
+                .with_attributes(item.command_name().map(|name| ("commandName", name)))
+                .with_attribute(("uri", image_name.as_str()))
+                .with_attribute(("type", "unknown"))
+                .write_empty()?;
+            Ok(())
+        })?;
+        Ok(())
+    }
+
+    fn write_graph<X>(
+        &mut self,
+        item: &Item,
+        graph: &Graph,
+        structure: &mut XmlWriter<X>,
+    ) -> Result<(), Error>
+    where
+        X: Write,
+    {
+        let graph = graph.to_spv();
+
+        let table_id = self.next_table_id();
+
+        let xml_name = format!("{table_id:011}_chart.xml");
+        self.writer
+            .start_file(&xml_name, SimpleFileOptions::default())?;
+        self.writer.write_all(&graph.xml)?;
+
+        let data_name = if let Some(data) = &graph.data {
+            let data_name = format!("{table_id:011}_chartData.bin");
+            self.writer
+                .start_file(&data_name, SimpleFileOptions::default())?;
+            self.writer.write_all(data)?;
+            Some(data_name)
+        } else {
+            None
+        };
+
+        let csv_name = if let Some(csv) = &graph.csv {
+            let data_name = format!("{table_id:011}_chartData.csv.bin");
+            self.writer
+                .start_file(&data_name, SimpleFileOptions::default())?;
+            self.writer.write_all(csv)?;
+            Some(data_name)
+        } else {
+            None
+        };
+
+        self.container(structure, item, "vgr:graph", |element| {
+            element
+                .with_attributes(item.command_name().map(|name| ("commandName", name)))
+                .with_attribute(("VDPId", "0"))
+                .write_inner_content(|w| {
+                    if let Some(data_name) = &data_name {
+                        w.create_element("vtb:dataPath")
+                            .write_text_content(BytesText::new(&data_name))?;
+                    }
+                    w.create_element("vtb:path")
+                        .write_text_content(BytesText::new(&xml_name))?;
+                    if let Some(csv_name) = &csv_name {
+                        w.create_element("vtb:csvPath")
+                            .write_text_content(BytesText::new(&csv_name))?;
+                    }
+                    Ok(())
+                })?;
+            Ok(())
+        })?;
+        Ok(())
+    }
+
     fn write_text<X>(
         &mut self,
         item: &Item,
@@ -188,23 +277,18 @@ where
         X: Write,
     {
         match &item.details {
-            Details::Graph | Details::Image(_) => todo!(),
+            Details::Image(image) => self.write_image(item, &image.0, structure),
+            Details::Graph(graph) => self.write_graph(item, graph, structure),
             Details::Heading(children) => {
-                let mut attributes = Vec::<Attribute>::new();
-                if let Some(command_name) = &item.command_name {
-                    attributes.push(("commandName", command_name.as_str()).into());
-                }
-                if !item.show {
-                    attributes.push(("visibility", "collapsed").into());
-                }
                 structure
                     .create_element("heading")
-                    .with_attributes(attributes)
+                    .with_attributes(item.command_name().map(|name| ("commandName", name)))
+                    .with_attributes(item.show.not().then_some(("visibility", "collapsed")))
                     .write_inner_content(|w| {
                         w.create_element("label")
                             .write_text_content(BytesText::new(&item.label()))?;
                         for child in &children.0 {
-                            self.write_item(&child, w).map_err(std::io::Error::other)?;
+                            self.write_item(child, w).map_err(std::io::Error::other)?;
                         }
                         Ok(())
                     })?;
@@ -237,13 +321,10 @@ where
             )
             .with_attribute(("visibility", if item.show { "visible" } else { "hidden" }))
             .write_inner_content(|w| {
-                let mut element = w
+                let element = w
                     .create_element("label")
                     .write_text_content(BytesText::new(&item.label()))?
                     .create_element(inner_elem);
-                if let Some(command_name) = &item.command_name {
-                    element = element.with_attribute(("commandName", command_name.as_str()));
-                };
                 closure(element).map_err(std::io::Error::other)?;
                 Ok(())
             })?;
@@ -570,7 +651,8 @@ where
                 "http://xml.spss.com/spss/viewer/viewer-pagesetup",
             ))
             .with_attribute(("xmlns:vtx", "http://xml.spss.com/spss/viewer/viewer-text"))
-            .with_attribute(("xmlns:vtb", "http://xml.spss.com/spss/viewer/viewer-table"));
+            .with_attribute(("xmlns:vtb", "http://xml.spss.com/spss/viewer/viewer-table"))
+            .with_attribute(("xmlns:vgr", "http://xml.spss.com/spss/viewer/viewer-graph"));
         element.write_inner_content(|w| {
             w.create_element("label")
                 .write_text_content(BytesText::new("Output"))?;
@@ -804,7 +886,7 @@ impl BinWrite for AreaStyle {
         let typeface = if self.font_style.font.is_empty() {
             "SansSerif"
         } else {
-            self.font_style.font.as_str()
+            &self.font_style.font
         };
         (
             (index + 1) as u8,
@@ -889,7 +971,7 @@ impl BinWrite for SpvBool {
     }
 }
 
-struct SpvString<T>(T);
+pub struct SpvString<T>(pub T);
 impl<'a> SpvString<&'a str> {
     fn optional(s: &'a Option<String>) -> Self {
         Self(s.as_ref().map_or("", |s| s.as_str()))
@@ -1044,7 +1126,7 @@ impl BinWrite for FontStyle {
         let typeface = if self.font.is_empty() {
             "SansSerif"
         } else {
-            self.font.as_str()
+            &self.font
         };
         (
             SpvBool(self.bold),
index 0f405fb4682a30af862a709fcc891563c90bf7b1..f467144bed466bcd20140bf0bd3470ad2934891d 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index cee6929d874a02f659168022610cd89e23961d95..7e92ae2ad23f8492c5ed88adec663789de5d0410 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -18,13 +18,13 @@ use std::{
     collections::BTreeMap,
     fmt::{Debug, Display},
     fs::File,
-    io::{BufRead, BufReader, Read, Seek},
+    io::{BufRead, Read, Seek},
     ops::Range,
     path::Path,
 };
 
 use crate::{
-    crypto::EncryptedReader,
+    crypto::MaybeEncryptedFile,
     data::{ByteString, Case, Datum, MutRawString, RawString},
     dictionary::{
         DictIndexMultipleResponseSet, DictIndexVariableSet, Dictionary, MrSetError,
@@ -521,46 +521,36 @@ impl<F> ReadOptions<F> {
     }
 
     /// Opens the file at `path`.
-    pub fn open_file<P>(mut self, path: P) -> Result<SystemFile, AnyError>
+    pub fn open_file<P>(self, path: P) -> Result<SystemFile, AnyError>
     where
         P: AsRef<Path>,
         F: FnMut(AnyError),
     {
-        let file = File::open(path)?;
-        if let Some(password) = self.password.take() {
-            // Don't create `BufReader`, because [EncryptedReader] will buffer.
-            self.open_reader_encrypted(file, password)
-        } else {
-            Self::open_reader_inner(BufReader::new(file), self.encoding, self.warn)
-        }
+        self.open_reader(File::open(path)?)
     }
 
     /// Opens the file read from `reader`.
-    fn open_reader_encrypted<R>(self, reader: R, password: String) -> Result<SystemFile, AnyError>
+    ///
+    /// This method will insert a [BufReader] internally, so the caller should
+    /// not.
+    ///
+    /// [BufReader]: std::io::BufReader
+    pub fn open_reader<R>(self, reader: R) -> Result<SystemFile, AnyError>
     where
         R: Read + Seek + 'static,
         F: FnMut(AnyError),
     {
         Self::open_reader_inner(
-            EncryptedReader::open(reader, password)?,
+            MaybeEncryptedFile::new(reader)
+                .map_err(|error| error.details)?
+                .with_password(self.password.as_ref())
+                .open_buffered()
+                .map_err(|error| error.details)?,
             self.encoding,
             self.warn,
         )
     }
 
-    /// Opens the file read from `reader`.
-    pub fn open_reader<R>(mut self, reader: R) -> Result<SystemFile, AnyError>
-    where
-        R: BufRead + Seek + 'static,
-        F: FnMut(AnyError),
-    {
-        if let Some(password) = self.password.take() {
-            self.open_reader_encrypted(reader, password)
-        } else {
-            Self::open_reader_inner(reader, self.encoding, self.warn)
-        }
-    }
-
     fn open_reader_inner<R>(
         reader: R,
         encoding: Option<&'static Encoding>,
index f4ee5f3c8d4d8a5d0c675a2671078a43f847f440..4ee7c0b3cd900de114ac2a60e5795f2f3a89cb2d 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -20,7 +20,7 @@
 
 use std::sync::LazyLock;
 
-use crate::locale_charset::locale_charset;
+use crate::locale::locale_charset;
 use encoding_rs::{Encoding, UTF_8};
 use serde::Serialize;
 use thiserror::Error as ThisError;
index 862d5a81feac5a70a9d5fab7eccb62c7ff900de1..f98b938599d45ffabf3073b4ed4887334b3c6d08 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -22,6 +22,7 @@
 use crate::{
     data::{ByteStr, ByteString, Datum, MutRawString, RawCase, RawString},
     endian::{FromBytes, ToBytes},
+    file::ReadSeek,
     identifier::{Error as IdError, Identifier},
     output::{
         Details, Item, Text,
@@ -1358,7 +1359,8 @@ impl<R> ZlibDecodeMultiple<R>
 where
     R: BufRead + Seek,
 {
-    fn new(reader: R, limit: u64) -> ZlibDecodeMultiple<R> {
+    fn new(mut reader: R, limit: u64) -> ZlibDecodeMultiple<R> {
+        dbg!(reader.stream_position().unwrap());
         ZlibDecodeMultiple {
             reader: Some(ZlibDecoder::new(reader)),
             limit,
@@ -1564,9 +1566,6 @@ where
     }
 }
 
-trait ReadSeek: Read + Seek {}
-impl<T> ReadSeek for T where T: Read + Seek {}
-
 /// Part of a string variable for reading data from a system file.
 ///
 /// A string variable in a system file is usually just the string itself
@@ -2139,19 +2138,19 @@ impl EncodingReport {
         .with_title("Character encoding information found in system file and its interpretation")
         .with_caption("A system file may identify its character encoding by name or by codepage number or both.  This table states which were found, how each was interpreted, and the overall interpretation.");
         if let Some((label, result)) = &self.name {
-            table.insert(&[0, 0], label.as_str());
-            table.insert(&[1, 0], result_to_value(result));
+            table.insert([0, 0], label.as_str());
+            table.insert([1, 0], result_to_value(result));
         } else {
-            table.insert(&[0, 0], "(none)");
+            table.insert([0, 0], "(none)");
         }
         if let Some((codepage, result)) = &self.codepage {
-            table.insert(&[0, 1], Value::new_integer(Some((*codepage) as f64)));
-            table.insert(&[1, 1], result_to_value(result));
+            table.insert([0, 1], Value::new_integer(Some((*codepage) as f64)));
+            table.insert([1, 1], result_to_value(result));
         } else {
-            table.insert(&[0, 1], "(none)");
+            table.insert([0, 1], "(none)");
         }
-        table.insert(&[0, 2], self.inferred_encoding_source.as_str());
-        table.insert(&[1, 2], result_to_value(&self.inferred_encoding));
+        table.insert([0, 2], self.inferred_encoding_source.as_str());
+        table.insert([1, 2], result_to_value(&self.inferred_encoding));
         table
     }
 }
index d2ccfccc365b756d264904dbea0dc472a7d2c26b..739c0e65e7b6480753cbcb15362f37c80542d97c 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
index 5177db530c345c7e94b05321b58a4e4b81f820de..b15c240f10b97bc9126d6a340477090dcc83cc0f 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -25,7 +25,7 @@ use encoding_rs::UTF_8;
 use itertools::Itertools;
 
 use crate::{
-    crypto::EncryptedFile,
+    crypto::{self, EncryptedFile},
     data::Datum,
     dictionary::Dictionary,
     identifier::Identifier,
@@ -33,12 +33,7 @@ use crate::{
         Item, Text,
         pivot::{Axis3, Dimension, Group, PivotTable, tests::assert_lines_eq, value::Value},
     },
-    sys::{
-        WriteOptions,
-        cooked::ReadOptions,
-        raw::{self, ErrorDetails, records::Compression},
-        sack::sack,
-    },
+    sys::{WriteOptions, cooked::ReadOptions, raw::records::Compression, sack::sack},
     variable::{VarWidth, Variable},
 };
 
@@ -576,12 +571,10 @@ fn encrypted_file_without_password() {
     })
     .open_file("src/crypto/testdata/test-encrypted.sav")
     .unwrap_err();
+    dbg!(&error);
     assert!(matches!(
-        error
-            .downcast::<raw::Error<ErrorDetails>>()
-            .unwrap()
-            .details,
-        ErrorDetails::Encrypted
+        error.downcast::<crypto::Error>().unwrap(),
+        crypto::Error::NeedPassword
     ));
 }
 
index 644d621db8e096138f108921e0d06f7d3b6c487b..58cb71d0501445472be55a32ab9a697db6e0f1f3 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software
@@ -1057,10 +1057,10 @@ where
                 zlib_writer.finish()?
             }
         };
-        if let Ok(n_cases) = u32::try_from(self.n_cases) {
-            if inner.seek(SeekFrom::Start(80)).is_ok() {
-                let _ = inner.write_all(&n_cases.to_le_bytes());
-            }
+        if let Ok(n_cases) = u32::try_from(self.n_cases)
+            && inner.seek(SeekFrom::Start(80)).is_ok()
+        {
+            let _ = inner.write_all(&n_cases.to_le_bytes());
         }
         Ok(Some(inner))
     }
index fca523b9750792bd88dc8d7052fc7e448b918d57..6bad552aff55aa41d4c9ab55371d922c3f90db5d 100644 (file)
@@ -1,5 +1,5 @@
 // PSPP - a program for statistical analysis.
-// Copyright (C) 2025 Free Software Foundation, Inc.
+// Copyright (C) 2025, 2026 Free Software Foundation, Inc.
 //
 // This program is free software: you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free Software