mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
5868 lines
229 KiB
Perl
Executable file
5868 lines
229 KiB
Perl
Executable file
#!/usr/bin/perl -w
|
|
#
|
|
# Generate code page .c files from ftp.unicode.org descriptions
|
|
#
|
|
# Copyright 2000 Alexandre Julliard
|
|
#
|
|
# This library 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 library 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 library; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
#
|
|
|
|
use strict;
|
|
use XML::LibXML;
|
|
use Digest::SHA;
|
|
use Encode;
|
|
use Time::Local qw(timegm_modern);
|
|
|
|
my $UNIVERSION = "15.1.0";
|
|
my $CLDRVERSION = "44";
|
|
my $ISO639VERSION = "20230123";
|
|
my $TZVERSION = "2024a";
|
|
|
|
my %data_files =
|
|
(
|
|
ucd => { url => "https://www.unicode.org/Public/$UNIVERSION/ucd/UCD.zip", name => "UCD-$UNIVERSION.zip",
|
|
sha => "cb1c663d053926500cd501229736045752713a066bd75802098598b7a7056177" },
|
|
unihan => { url => "https://www.unicode.org/Public/$UNIVERSION/ucd/Unihan.zip", name => "Unihan-$UNIVERSION.zip",
|
|
sha => "a0226610e324bcf784ac380e11f4cbf533ee1e6b3d028b0991bf8c0dc3f85853" },
|
|
idna => { url => "https://www.unicode.org/Public/idna/$UNIVERSION/IdnaMappingTable.txt", name => "IdnaMappingTable-$UNIVERSION.txt",
|
|
sha => "402cbd285f1f952fcd0834b63541d54f69d3d8f1b8f8599bf71a1a14935f82c4" },
|
|
cldr => { url => "https://github.com/unicode-org/cldr/archive/refs/tags/release-$CLDRVERSION.zip",
|
|
sha => "38d04cf28ccfee8b86d2feecebf99d5dc6d3317f53f87ba53b1e774f6395573c" },
|
|
cldr33 => { url => "https://www.unicode.org/Public/cldr/33/cldr-common-33.0.zip",
|
|
sha => "fa3490082c086d21257153609642f54fcf788fcfda4966fe67f3f6daca0d58b9" },
|
|
sorting => { url => "https://download.microsoft.com/download/C/F/7/CF713A5E-9FBC-4FD6-9246-275F65C0E498/Windows 10 Sorting Weight Table.txt",
|
|
sha => "81fcfa1e5ed3e3a94d329959ff7d97d522ddf9d653d2c4d6ddcccc5cd4df663f" },
|
|
codepages => { url => "https://download.microsoft.com/download/C/F/7/CF713A5E-9FBC-4FD6-9246-275F65C0E498/Windows Supported Code Page Data Files.zip",
|
|
sha => "5074e6dd253056ba61fc6c870c9a955467855129c6ad3a51761c386b301b125a" },
|
|
iso639 => { url => "https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3_Code_Tables_$ISO639VERSION.zip",
|
|
sha => "884faa6cc5ac5181ed7969eed75355c1bc665447614cf4c06c62e87b38fe6a97" },
|
|
ksx1001 => { url => "https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/KSC/KSX1001.TXT",
|
|
sha => "d8d2a35206ac0ea2865f5d801c9d6717f735bf46f263a658a64a960abe59e371" },
|
|
jis0208 => { url => "https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0208.TXT",
|
|
sha => "1c571870457f19c97720631fa83ee491549a96ba1436da1296786a67d8632e87" },
|
|
jis0212 => { url => "https://www.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/JIS0212.TXT",
|
|
sha => "477820bb3055bbcc90880d788cd95607d221dc94457bae249231adecf13c12e6" },
|
|
tzdata => { url => "https://data.iana.org/time-zones/releases/tzdata$TZVERSION.tar.gz",
|
|
sha => "0d0434459acbd2059a7a8da1f3304a84a86591f6ed69c6248fffa502b6edffe3" },
|
|
);
|
|
|
|
|
|
# Default char for undefined mappings
|
|
my $DEF_CHAR = ord '?';
|
|
|
|
# Last valid Unicode character
|
|
my $MAX_CHAR = 0x10ffff;
|
|
|
|
my $nlskey = "-SYSTEM\\-CurrentControlSet\\-Control\\-Nls";
|
|
my $zonekey = "-Software\\-Microsoft\\-Windows NT\\-CurrentVersion\\Time Zones";
|
|
|
|
my @allfiles =
|
|
(
|
|
"CodpageFiles/037.txt",
|
|
"CodpageFiles/437.txt",
|
|
"CodpageFiles/500.txt",
|
|
"CodpageFiles/708.txt",
|
|
"CodpageFiles/720.txt",
|
|
"CodpageFiles/737.txt",
|
|
"CodpageFiles/775.txt",
|
|
"CodpageFiles/850.txt",
|
|
"CodpageFiles/852.txt",
|
|
"CodpageFiles/855.txt",
|
|
"CodpageFiles/857.txt",
|
|
"CodpageFiles/860.txt",
|
|
"CodpageFiles/861.txt",
|
|
"CodpageFiles/862.txt",
|
|
"CodpageFiles/863.txt",
|
|
"CodpageFiles/864.txt",
|
|
"CodpageFiles/865.txt",
|
|
"CodpageFiles/866.txt",
|
|
"CodpageFiles/869.txt",
|
|
"CodpageFiles/874.txt",
|
|
"CodpageFiles/875.txt",
|
|
"CodpageFiles/932.txt",
|
|
"CodpageFiles/936.txt",
|
|
"CodpageFiles/949.txt",
|
|
"CodpageFiles/950.txt",
|
|
"CodpageFiles/1026.txt",
|
|
"CodpageFiles/1250.txt",
|
|
"CodpageFiles/1251.txt",
|
|
"CodpageFiles/1252.txt",
|
|
"CodpageFiles/1253.txt",
|
|
"CodpageFiles/1254.txt",
|
|
"CodpageFiles/1255.txt",
|
|
"CodpageFiles/1256.txt",
|
|
"CodpageFiles/1257.txt",
|
|
"CodpageFiles/1258.txt",
|
|
"CodpageFiles/1361.txt",
|
|
"CodpageFiles/10000.txt",
|
|
"CodpageFiles/10001.txt",
|
|
"CodpageFiles/10002.txt",
|
|
"CodpageFiles/10003.txt",
|
|
"CodpageFiles/10004.txt",
|
|
"CodpageFiles/10005.txt",
|
|
"CodpageFiles/10006.txt",
|
|
"CodpageFiles/10007.txt",
|
|
"CodpageFiles/10008.txt",
|
|
"CodpageFiles/10010.txt",
|
|
"CodpageFiles/10017.txt",
|
|
"CodpageFiles/10021.txt",
|
|
"CodpageFiles/10029.txt",
|
|
"CodpageFiles/10079.txt",
|
|
"CodpageFiles/10081.txt",
|
|
"CodpageFiles/10082.txt",
|
|
"CodpageFiles/20127.txt",
|
|
"CodpageFiles/20866.txt",
|
|
"CodpageFiles/21866.txt",
|
|
"CodpageFiles/28591.txt",
|
|
"CodpageFiles/28592.txt",
|
|
"CodpageFiles/28593.txt",
|
|
"CodpageFiles/28594.txt",
|
|
"CodpageFiles/28595.txt",
|
|
"CodpageFiles/28596.txt",
|
|
"CodpageFiles/28597.txt",
|
|
"CodpageFiles/28598.txt",
|
|
"CodpageFiles/28599.txt",
|
|
"CodpageFiles/28603.txt",
|
|
"CodpageFiles/28605.txt",
|
|
);
|
|
|
|
my @timezone_files = qw(africa antarctica asia australasia europe northamerica southamerica etcetera backward);
|
|
|
|
my %ctype =
|
|
(
|
|
# CT_CTYPE1
|
|
"upper" => 0x0001,
|
|
"lower" => 0x0002,
|
|
"digit" => 0x0004,
|
|
"space" => 0x0008,
|
|
"punct" => 0x0010,
|
|
"cntrl" => 0x0020,
|
|
"blank" => 0x0040,
|
|
"xdigit" => 0x0080,
|
|
"alpha" => 0x0100 | 0x80000000,
|
|
"defin" => 0x0200,
|
|
# CT_CTYPE3 in high 16 bits
|
|
"nonspacing" => 0x00010000,
|
|
"diacritic" => 0x00020000,
|
|
"vowelmark" => 0x00040000,
|
|
"symbol" => 0x00080000,
|
|
"katakana" => 0x00100000,
|
|
"hiragana" => 0x00200000,
|
|
"halfwidth" => 0x00400000,
|
|
"fullwidth" => 0x00800000,
|
|
"ideograph" => 0x01000000,
|
|
"kashida" => 0x02000000,
|
|
"lexical" => 0x04000000,
|
|
"highsurrogate" => 0x08000000,
|
|
"lowsurrogate" => 0x10000000,
|
|
);
|
|
|
|
my %bracket_types =
|
|
(
|
|
"o" => 0x0000,
|
|
"c" => 0x0001,
|
|
);
|
|
|
|
my %indic_types =
|
|
(
|
|
"Other" => 0x0000,
|
|
"Bindu" => 0x0001,
|
|
"Visarga" => 0x0002,
|
|
"Avagraha" => 0x0003,
|
|
"Nukta" => 0x0004,
|
|
"Virama" => 0x0005,
|
|
"Vowel_Independent" => 0x0006,
|
|
"Vowel_Dependent" => 0x0007,
|
|
"Vowel" => 0x0008,
|
|
"Consonant_Placeholder" => 0x0009,
|
|
"Consonant" => 0x000a,
|
|
"Consonant_Dead" => 0x000b,
|
|
"Consonant_Succeeding_Repha" => 0x000c,
|
|
"Consonant_Subjoined" => 0x000d,
|
|
"Consonant_Medial" => 0x000e,
|
|
"Consonant_Final" => 0x000f,
|
|
"Consonant_Head_Letter" => 0x0010,
|
|
"Modifying_Letter" => 0x0011,
|
|
"Tone_Letter" => 0x0012,
|
|
"Tone_Mark" => 0x0013,
|
|
"Register_Shifter" => 0x0014,
|
|
"Consonant_Preceding_Repha" => 0x0015,
|
|
"Pure_Killer" => 0x0016,
|
|
"Invisible_Stacker" => 0x0017,
|
|
"Gemination_Mark" => 0x0018,
|
|
"Cantillation_Mark" => 0x0019,
|
|
"Non_Joiner" => 0x001a,
|
|
"Joiner" => 0x001b,
|
|
"Number_Joiner" => 0x001c,
|
|
"Number" => 0x001d,
|
|
"Brahmi_Joining_Number" => 0x001e,
|
|
"Consonant_With_Stacker" => 0x001f,
|
|
"Consonant_Prefixed" => 0x0020,
|
|
"Syllable_Modifier" => 0x0021,
|
|
"Consonant_Killer" => 0x0022,
|
|
"Consonant_Initial_Postfixed" => 0x0023,
|
|
);
|
|
|
|
my %matra_types =
|
|
(
|
|
"Right" => 0x01,
|
|
"Left" => 0x02,
|
|
"Visual_Order_Left" => 0x03,
|
|
"Left_And_Right" => 0x04,
|
|
"Top" => 0x05,
|
|
"Bottom" => 0x06,
|
|
"Top_And_Bottom" => 0x07,
|
|
"Top_And_Right" => 0x08,
|
|
"Top_And_Left" => 0x09,
|
|
"Top_And_Left_And_Right" => 0x0a,
|
|
"Bottom_And_Right" => 0x0b,
|
|
"Top_And_Bottom_And_Right" => 0x0c,
|
|
"Overstruck" => 0x0d,
|
|
"Invisible" => 0x0e,
|
|
"Bottom_And_Left" => 0x0f,
|
|
"Top_And_Bottom_And_Left" => 0x10,
|
|
);
|
|
|
|
my %break_types =
|
|
(
|
|
"BK" => 0x0001,
|
|
"CR" => 0x0002,
|
|
"LF" => 0x0003,
|
|
"CM" => 0x0004,
|
|
"SG" => 0x0005,
|
|
"GL" => 0x0006,
|
|
"CB" => 0x0007,
|
|
"SP" => 0x0008,
|
|
"ZW" => 0x0009,
|
|
"NL" => 0x000a,
|
|
"WJ" => 0x000b,
|
|
"JL" => 0x000c,
|
|
"JV" => 0x000d,
|
|
"JT" => 0x000e,
|
|
"H2" => 0x000f,
|
|
"H3" => 0x0010,
|
|
"XX" => 0x0011,
|
|
"OP" => 0x0012,
|
|
"CL" => 0x0013,
|
|
"CP" => 0x0014,
|
|
"QU" => 0x0015,
|
|
"NS" => 0x0016,
|
|
"EX" => 0x0017,
|
|
"SY" => 0x0018,
|
|
"IS" => 0x0019,
|
|
"PR" => 0x001a,
|
|
"PO" => 0x001b,
|
|
"NU" => 0x001c,
|
|
"AL" => 0x001d,
|
|
"ID" => 0x001e,
|
|
"IN" => 0x001f,
|
|
"HY" => 0x0020,
|
|
"BB" => 0x0021,
|
|
"BA" => 0x0022,
|
|
"SA" => 0x0023,
|
|
"AI" => 0x0024,
|
|
"B2" => 0x0025,
|
|
"HL" => 0x0026,
|
|
"CJ" => 0x0027,
|
|
"RI" => 0x0028,
|
|
"EB" => 0x0029,
|
|
"EM" => 0x002a,
|
|
"ZWJ" => 0x002b,
|
|
"AK" => 0x002c,
|
|
"AP" => 0x002d,
|
|
"AS" => 0x002e,
|
|
"VF" => 0x002f,
|
|
"VI" => 0x0030,
|
|
);
|
|
|
|
my %vertical_types =
|
|
(
|
|
"R" => 0x0000,
|
|
"U" => 0x0001,
|
|
"Tr" => 0x0002,
|
|
"Tu" => 0x0003,
|
|
);
|
|
|
|
my %categories =
|
|
(
|
|
"Lu" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"upper"}, # Letter, Uppercase
|
|
"Ll" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"lower"}, # Letter, Lowercase
|
|
"Lt" => $ctype{"defin"}|$ctype{"alpha"}|$ctype{"upper"}|$ctype{"lower"}, # Letter, Titlecase
|
|
"Mn" => $ctype{"defin"}|$ctype{"nonspacing"}, # Mark, Non-Spacing
|
|
"Mc" => $ctype{"defin"}, # Mark, Spacing Combining
|
|
"Me" => $ctype{"defin"}, # Mark, Enclosing
|
|
"Nd" => $ctype{"defin"}|$ctype{"digit"}, # Number, Decimal Digit
|
|
"Nl" => $ctype{"defin"}|$ctype{"alpha"}, # Number, Letter
|
|
"No" => $ctype{"defin"}, # Number, Other
|
|
"Zs" => $ctype{"defin"}|$ctype{"space"}, # Separator, Space
|
|
"Zl" => $ctype{"defin"}|$ctype{"space"}, # Separator, Line
|
|
"Zp" => $ctype{"defin"}|$ctype{"space"}, # Separator, Paragraph
|
|
"Cc" => $ctype{"defin"}|$ctype{"cntrl"}, # Other, Control
|
|
"Cf" => $ctype{"defin"}|$ctype{"cntrl"}, # Other, Format
|
|
"Cs" => $ctype{"defin"}, # Other, Surrogate
|
|
"Co" => $ctype{"defin"}, # Other, Private Use
|
|
"Cn" => $ctype{"defin"}, # Other, Not Assigned
|
|
"Lm" => $ctype{"defin"}|$ctype{"alpha"}, # Letter, Modifier
|
|
"Lo" => $ctype{"defin"}|$ctype{"alpha"}, # Letter, Other
|
|
"Pc" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Connector
|
|
"Pd" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Dash
|
|
"Ps" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Open
|
|
"Pe" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Close
|
|
"Pi" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Initial quote
|
|
"Pf" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Final quote
|
|
"Po" => $ctype{"defin"}|$ctype{"punct"}, # Punctuation, Other
|
|
"Sm" => $ctype{"defin"}|$ctype{"symbol"}, # Symbol, Math
|
|
"Sc" => $ctype{"defin"}|$ctype{"symbol"}, # Symbol, Currency
|
|
"Sk" => $ctype{"defin"}|$ctype{"symbol"}, # Symbol, Modifier
|
|
"So" => $ctype{"defin"}|$ctype{"symbol"} # Symbol, Other
|
|
);
|
|
|
|
# a few characters need additional categories that cannot be determined automatically
|
|
my %special_categories =
|
|
(
|
|
"xdigit" => [ ord('0')..ord('9'),ord('A')..ord('F'),ord('a')..ord('f'),
|
|
0xff10..0xff19, 0xff21..0xff26, 0xff41..0xff46 ],
|
|
"space" => [ 0x09..0x0d, 0x85 ],
|
|
"blank" => [ 0x09, 0x20, 0xa0, 0x3000, 0xfeff ],
|
|
"cntrl" => [ 0x070f, 0x200c, 0x200d,
|
|
0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e,
|
|
0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
|
|
0xfff9, 0xfffa, 0xfffb ],
|
|
"punct" => [ 0x24, 0x2b, 0x3c..0x3e, 0x5e, 0x60, 0x7c, 0x7e, 0xa2..0xbe,
|
|
0xd7, 0xf7 ],
|
|
"digit" => [ 0xb2, 0xb3, 0xb9 ],
|
|
"lower" => [ 0xaa, 0xba, 0x2071, 0x207f ],
|
|
"nonspacing" => [ 0xc0..0xc5, 0xc7..0xcf, 0xd1..0xd6, 0xd8..0xdd, 0xe0..0xe5, 0xe7..0xef,
|
|
0xf1..0xf6, 0xf8..0xfd, 0xff, 0x6de, 0x1929..0x192b, 0x302e..0x302f ],
|
|
"diacritic" => [ 0x5e, 0x60, 0xb7, 0xd8, 0xf8 ],
|
|
"symbol" => [ 0x09..0x0d, 0x20..0x23, 0x25, 0x26, 0x28..0x2a, 0x2c, 0x2e..0x2f, 0x3a..0x40,
|
|
0x5b..0x60, 0x7b..0x7e, 0xa0..0xa9, 0xab..0xb1, 0xb4..0xb8, 0xbb, 0xbf,
|
|
0x02b9..0x02ba, 0x02c6..0x02cf ],
|
|
"halfwidth" => [ 0x20..0x7e, 0xa2..0xa3, 0xa5..0xa6, 0xac, 0xaf, 0x20a9 ],
|
|
"fullwidth" => [ 0x2018..0x2019, 0x201c..0x201d, 0x3000..0x3002, 0x300c..0x300d, 0x309b..0x309c,
|
|
0x30a1..0x30ab, 0x30ad, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9,
|
|
0x30bb, 0x30bd, 0x30bf, 0x30c1, 0x30c3, 0x30c4, 0x30c6, 0x30c8, 0x30ca..0x30cf,
|
|
0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de..0x30ed, 0x30ef, 0x30f2..0x30f3, 0x30fb,
|
|
0x3131..0x3164 ],
|
|
"ideograph" => [ 0x3006..0x3007 ],
|
|
"lexical" => [ 0x22, 0x24, 0x27, 0x2d, 0x2f, 0x3d, 0x40, 0x5c, 0x5e..0x60, 0x7e,
|
|
0xa8, 0xaa, 0xad, 0xaf, 0xb4, 0xb8, 0xba,
|
|
0x02b0..0x02b8, 0x02bc, 0x02c7, 0x02ca..0x02cb, 0x02cf, 0x02d8..0x02dd, 0x02e0..0x02e3,
|
|
0x037a, 0x0384..0x0385, 0x0387, 0x0559..0x055a, 0x0640, 0x1fbd..0x1fc1,
|
|
0x1fcd..0x1fcf, 0x1fdd..0x1fdf, 0x1fed..0x1fef, 0x1ffd..0x1ffe, 0x2010..0x2015,
|
|
0x2032..0x2034, 0x2038, 0x2043..0x2044, 0x207b..0x207c, 0x207f, 0x208b..0x208c,
|
|
0x2212, 0x2215..0x2216, 0x2500, 0x2504..0x2505, 0x2508..0x2509, 0x254c..0x254d,
|
|
0x3003, 0x301c, 0x3030..0x3035, 0x309b..0x309e, 0x30fd..0x30fe, 0xfe31..0xfe32,
|
|
0xfe58, 0xfe63, 0xfe66, 0xfe68..0xfe69, 0xfe6b, 0xff04, 0xff07, 0xff0d, 0xff0f,
|
|
0xff1d, 0xff20, 0xff3c, 0xff3e, 0xff40, 0xff5e ],
|
|
"kashida" => [ 0x0640 ],
|
|
);
|
|
|
|
my %directions =
|
|
(
|
|
"L" => 1, # Left-to-Right
|
|
"R" => 2, # Right-to-Left
|
|
"AL" => 12, # Right-to-Left Arabic
|
|
"EN" => 3, # European Number
|
|
"ES" => 4, # European Number Separator
|
|
"ET" => 5, # European Number Terminator
|
|
"AN" => 6, # Arabic Number
|
|
"CS" => 7, # Common Number Separator
|
|
"NSM" => 13, # Non-Spacing Mark
|
|
"BN" => 14, # Boundary Neutral
|
|
"B" => 8, # Paragraph Separator
|
|
"S" => 9, # Segment Separator
|
|
"WS" => 10, # Whitespace
|
|
"ON" => 11, # Other Neutrals
|
|
"LRE" => 15, # Left-to-Right Embedding
|
|
"LRO" => 15, # Left-to-Right Override
|
|
"RLE" => 15, # Right-to-Left Embedding
|
|
"RLO" => 15, # Right-to-Left Override
|
|
"PDF" => 15, # Pop Directional Format
|
|
"LRI" => 15, # Left-to-Right Isolate
|
|
"RLI" => 15, # Right-to-Left Isolate
|
|
"FSI" => 15, # First Strong Isolate
|
|
"PDI" => 15 # Pop Directional Isolate
|
|
);
|
|
|
|
my %c2_types =
|
|
(
|
|
"L" => 1, # C2_LEFTTORIGHT
|
|
"R" => 2, # C2_RIGHTTOLEFT
|
|
"AL" => 2, # C2_RIGHTTOLEFT
|
|
"EN" => 3, # C2_EUROPENUMBER
|
|
"ES" => 4, # C2_EUROPESEPARATOR
|
|
"ET" => 5, # C2_EUROPETERMINATOR
|
|
"AN" => 6, # C2_ARABICNUMBER
|
|
"CS" => 7, # C2_COMMONSEPARATOR
|
|
"NSM" => 11, # C2_OTHERNEUTRAL
|
|
"BN" => 0, # C2_NOTAPPLICABLE
|
|
"B" => 8, # C2_BLOCKSEPARATOR
|
|
"S" => 9, # C2_SEGMENTSEPARATOR
|
|
"WS" => 10, # C2_WHITESPACE
|
|
"ON" => 11, # C2_OTHERNEUTRAL
|
|
"LRE" => 11, # C2_OTHERNEUTRAL
|
|
"LRO" => 11, # C2_OTHERNEUTRAL
|
|
"RLE" => 11, # C2_OTHERNEUTRAL
|
|
"RLO" => 11, # C2_OTHERNEUTRAL
|
|
"PDF" => 11, # C2_OTHERNEUTRAL
|
|
"LRI" => 11, # C2_OTHERNEUTRAL
|
|
"RLI" => 11, # C2_OTHERNEUTRAL
|
|
"FSI" => 11, # C2_OTHERNEUTRAL
|
|
"PDI" => 11 # C2_OTHERNEUTRAL
|
|
);
|
|
|
|
my %bidi_types =
|
|
(
|
|
"ON" => 0, # Other Neutrals
|
|
"L" => 1, # Left-to-Right
|
|
"R" => 2, # Right-to-Left
|
|
"AN" => 3, # Arabic Number
|
|
"EN" => 4, # European Number
|
|
"AL" => 5, # Right-to-Left Arabic
|
|
"NSM" => 6, # Non-Spacing Mark
|
|
"CS" => 7, # Common Number Separator
|
|
"ES" => 8, # European Number Separator
|
|
"ET" => 9, # European Number Terminator
|
|
"BN" => 10, # Boundary Neutral
|
|
"S" => 11, # Segment Separator
|
|
"WS" => 12, # Whitespace
|
|
"B" => 13, # Paragraph Separator
|
|
"RLO" => 14, # Right-to-Left Override
|
|
"RLE" => 15, # Right-to-Left Embedding
|
|
"LRO" => 16, # Left-to-Right Override
|
|
"LRE" => 17, # Left-to-Right Embedding
|
|
"PDF" => 18, # Pop Directional Format
|
|
"LRI" => 19, # Left-to-Right Isolate
|
|
"RLI" => 20, # Right-to-Left Isolate
|
|
"FSI" => 21, # First Strong Isolate
|
|
"PDI" => 22 # Pop Directional Isolate
|
|
);
|
|
|
|
my %joining_types =
|
|
(
|
|
"U" => 0, # Non_Joining
|
|
"L" => 1, # Left_Joining
|
|
"R" => 2, # Right_Joining
|
|
"D" => 3, # Dual_Joining
|
|
"C" => 3, # Join_Causing
|
|
"ALAPH" => 4, # Syriac ALAPH
|
|
"DALATH RISH" => 5, # Syriac DALATH RISH group
|
|
"T" => 6, # Transparent
|
|
);
|
|
|
|
my @locales =
|
|
(
|
|
{ name => "", lcid => 0x0000007f, file => "root", territory => "IV", sabbrevlangname => "IVL", sopentypelang =>"dflt" },
|
|
{ name => "aa", sopentypelang => "AFR" },
|
|
{ name => "aa-DJ" },
|
|
{ name => "aa-ER" },
|
|
{ name => "aa-ET" },
|
|
{ name => "af", lcid => 0x00000036, oemcp => 850, sabbrevlangname => "AFK", sopentypelang => "AFK" },
|
|
{ name => "af-NA" },
|
|
{ name => "af-ZA", lcid => 0x00000436 },
|
|
{ name => "agq" },
|
|
{ name => "agq-CM" },
|
|
{ name => "ak", sopentypelang => "TWI" },
|
|
{ name => "ak-GH" },
|
|
{ name => "am", lcid => 0x0000005e, sabbrevlangname => "AMH" },
|
|
{ name => "am-ET", lcid => 0x0000045e },
|
|
{ name => "ar", lcid => 0x00000001, territory => "SA", oemcp => 720, group => 13 },
|
|
{ name => "ar-001" },
|
|
{ name => "ar-AE", lcid => 0x00003801, sabbrevlangname => "ARU" },
|
|
{ name => "ar-BH", lcid => 0x00003c01, sabbrevlangname => "ARH" },
|
|
{ name => "ar-DJ" },
|
|
{ name => "ar-DZ", lcid => 0x00001401, sabbrevlangname => "ARG", nativedigits => "0123456789" },
|
|
{ name => "ar-EG", lcid => 0x00000c01, sabbrevlangname => "ARE" },
|
|
{ name => "ar-EH" },
|
|
{ name => "ar-ER" },
|
|
{ name => "ar-IL" },
|
|
{ name => "ar-IQ", lcid => 0x00000801, sabbrevlangname => "ARI" },
|
|
{ name => "ar-JO", lcid => 0x00002c01, sabbrevlangname => "ARJ" },
|
|
{ name => "ar-KM" },
|
|
{ name => "ar-KW", lcid => 0x00003401, sabbrevlangname => "ARK" },
|
|
{ name => "ar-LB", lcid => 0x00003001, sabbrevlangname => "ARB" },
|
|
{ name => "ar-LY", lcid => 0x00001001, sabbrevlangname => "ARL", nativedigits => "0123456789" },
|
|
{ name => "ar-MA", lcid => 0x00001801, sabbrevlangname => "ARM", nativedigits => "0123456789" },
|
|
{ name => "ar-MR" },
|
|
{ name => "ar-OM", lcid => 0x00002001, sabbrevlangname => "ARO" },
|
|
{ name => "ar-PS" },
|
|
{ name => "ar-QA", lcid => 0x00004001, sabbrevlangname => "ARQ" },
|
|
{ name => "ar-SA", lcid => 0x00000401, sabbrevlangname => "ARA" },
|
|
{ name => "ar-SD" },
|
|
{ name => "ar-SO" },
|
|
{ name => "ar-SS" },
|
|
{ name => "ar-SY", lcid => 0x00002801, sabbrevlangname => "ARS" },
|
|
{ name => "ar-TD" },
|
|
{ name => "ar-TN", lcid => 0x00001c01, sabbrevlangname => "ART", nativedigits => "0123456789" },
|
|
{ name => "ar-YE", lcid => 0x00002401, sabbrevlangname => "ARY" },
|
|
{ name => "arn", lcid => 0x0000007a, oemcp => 850, ebcdiccp => 20284, slist => ",", sabbrevlangname => "MPD", sopentypelang => "MAP" },
|
|
{ name => "arn-CL", lcid => 0x0000047a },
|
|
{ name => "arn-Latn", alias => "arn" },
|
|
{ name => "arn-Latn-CL", alias => "arn-CL" },
|
|
{ name => "as", lcid => 0x0000004d, slist => ",", group => 15 },
|
|
{ name => "as-IN", lcid => 0x0000044d },
|
|
{ name => "asa" },
|
|
{ name => "asa-TZ" },
|
|
{ name => "ast" },
|
|
{ name => "ast-ES" },
|
|
{ name => "az", lcid => 0x0000002c, oemcp => 857, ebcdiccp => 20905, group => 2 },
|
|
{ name => "az-Cyrl", lcid => 0x0000742c, oemcp => 866, ebcdiccp => 20880, group => 5, sabbrevlangname => "AZC" },
|
|
{ name => "az-Cyrl-AZ", lcid => 0x0000082c },
|
|
{ name => "az-Latn", lcid => 0x0000782c },
|
|
{ name => "az-Latn-AZ", lcid => 0x0000042c },
|
|
{ name => "ba", lcid => 0x0000006d, oemcp => 866, group => 5, sabbrevlangname => "BAS", sopentypelang => "BSH" },
|
|
{ name => "ba-Cyrl", alias => "ba" },
|
|
{ name => "ba-Cyrl-RU", alias => "ba-RU" },
|
|
{ name => "ba-RU", lcid => 0x0000046d },
|
|
{ name => "bas" },
|
|
{ name => "bas-CM" },
|
|
{ name => "be", lcid => 0x00000023, oemcp => 866, ebcdiccp => 500, group => 5 },
|
|
{ name => "be-BY", lcid => 0x00000423 },
|
|
{ name => "bem" },
|
|
{ name => "bem-ZM" },
|
|
{ name => "bew" },
|
|
{ name => "bew-ID" },
|
|
{ name => "bez" },
|
|
{ name => "bez-TZ" },
|
|
{ name => "bg", lcid => 0x00000002, oemcp => 866, ebcdiccp => 21025, group => 5, sabbrevlangname => "BGR", sopentypelang => "BGR" },
|
|
{ name => "bg-BG", lcid => 0x00000402 },
|
|
{ name => "bin", lcid => 0x00000066, oemcp => 850, dir => "exemplars", sabbrevlangname => "ZZZ", sopentypelang => "EDO" },
|
|
{ name => "bin-NG", lcid => 0x00000466, file => "bin", dir => "exemplars" },
|
|
{ name => "blo" },
|
|
{ name => "blo-BJ" },
|
|
{ name => "bm", sopentypelang => "BMB" },
|
|
{ name => "bm-Latn", file => "bm" },
|
|
{ name => "bm-Latn-ML", file => "bm_ML" },
|
|
{ name => "bm-ML", alias => "bm-Latn-ML" },
|
|
{ name => "bn", lcid => 0x00000045, slist => ",", group => 15, sabbrevlangname => "BNB" },
|
|
{ name => "bn-BD", lcid => 0x00000845 },
|
|
{ name => "bn-IN", lcid => 0x00000445, sabbrevlangname => "BNG" },
|
|
{ name => "bo", lcid => 0x00000051, slist => ",", group => 15, sabbrevlangname => "BOB", sopentypelang => "TIB" },
|
|
{ name => "bo-CN", lcid => 0x00000451 },
|
|
{ name => "bo-IN", slist => "," },
|
|
{ name => "bo-Tibt", alias => "bo" },
|
|
{ name => "bo-Tibt-CN", alias => "bo-CN" },
|
|
{ name => "bo-Tibt-IN", alias => "bo-IN" },
|
|
{ name => "br", lcid => 0x0000007e, oemcp => 850, ebcdiccp => 20297 },
|
|
{ name => "br-FR", lcid => 0x0000047e },
|
|
{ name => "br-Latn", alias => "br" },
|
|
{ name => "br-Latn-FR", alias => "br-FR" },
|
|
{ name => "brx" },
|
|
{ name => "brx-IN" },
|
|
{ name => "bs", lcid => 0x0000781a, oemcp => 852, maccp => 10082, ebcdiccp => 870, group => 2, sabbrevlangname => "BSB" },
|
|
{ name => "bs-Cyrl", lcid => 0x0000641a, oemcp => 855, group => 5, sabbrevlangname => "BSC" },
|
|
{ name => "bs-Cyrl-BA", lcid => 0x0000201a },
|
|
{ name => "bs-Latn", lcid => 0x0000681a },
|
|
{ name => "bs-Latn-BA", lcid => 0x0000141a },
|
|
{ name => "byn", sopentypelang => "BIL" },
|
|
{ name => "byn-ER" },
|
|
{ name => "ca", lcid => 0x00000003, oemcp => 850 },
|
|
{ name => "ca-AD", maccp => 65001 },
|
|
{ name => "ca-ES", lcid => 0x00000403 },
|
|
{ name => "ca-ES-valencia", lcid => 0x00000803, file => "ca_ES_VALENCIA", sabbrevlangname => "VAL" },
|
|
{ name => "ca-FR", maccp => 65001 },
|
|
{ name => "ca-IT", maccp => 65001 },
|
|
{ name => "ccp" },
|
|
{ name => "ccp-BD", alias => "ccp-Cakm-BD" },
|
|
{ name => "ccp-Cakm", file => "ccp" },
|
|
{ name => "ccp-Cakm-BD", file => "ccp_BD" },
|
|
{ name => "ccp-Cakm-IN", file => "ccp_IN" },
|
|
{ name => "ccp-IN", alias => "ccp-Cakm-IN" },
|
|
{ name => "ce" },
|
|
{ name => "ce-RU" },
|
|
{ name => "ceb" },
|
|
{ name => "ceb-Latn", file => "ceb" },
|
|
{ name => "ceb-Latn-PH", file => "ceb_PH" },
|
|
{ name => "ceb-PH", alias => "ceb-Latn-PH" },
|
|
{ name => "cgg" },
|
|
{ name => "cgg-UG" },
|
|
{ name => "chr", lcid => 0x0000005c, slist => ",", sabbrevlangname => "CRE" },
|
|
{ name => "chr-Cher", lcid => 0x00007c5c, file => "chr" },
|
|
{ name => "chr-Cher-US", lcid => 0x0000045c, file => "chr_US" },
|
|
{ name => "chr-US", alias => "chr-Cher-US" },
|
|
{ name => "ckb", alias => "ku" },
|
|
{ name => "ckb-IQ", alias => "ku-Arab-IQ" },
|
|
{ name => "ckb-IR", alias => "ku-Arab-IR" },
|
|
{ name => "co", lcid => 0x00000083, oemcp => 850, ebcdiccp => 20297 },
|
|
{ name => "co-FR", lcid => 0x00000483 },
|
|
{ name => "co-Latn", alias => "co" },
|
|
{ name => "co-Latn-FR", alias => "co-FR" },
|
|
{ name => "cs", lcid => 0x00000005, oemcp => 852, group => 2, sabbrevlangname => "CSY", sopentypelang => "CSY" },
|
|
{ name => "cs-CZ", lcid => 0x00000405 },
|
|
{ name => "csw" },
|
|
{ name => "csw-CA" },
|
|
{ name => "cu", sopentypelang => "CSL" },
|
|
{ name => "cu-RU" },
|
|
{ name => "cy", lcid => 0x00000052, oemcp => 850, ebcdiccp => 20285, sabbrevlangname => "CYM", sopentypelang => "WEL" },
|
|
{ name => "cy-GB", lcid => 0x00000452 },
|
|
{ name => "da", lcid => 0x00000006, oemcp => 850, ebcdiccp => 20277 },
|
|
{ name => "da-DK", lcid => 0x00000406 },
|
|
{ name => "da-GL", maccp => 65001 },
|
|
{ name => "dav" },
|
|
{ name => "dav-KE" },
|
|
{ name => "de", lcid => 0x00000007, oemcp => 850, ebcdiccp => 20273 },
|
|
{ name => "de-AT", lcid => 0x00000c07, sabbrevlangname => "DEA" },
|
|
{ name => "de-BE" },
|
|
{ name => "de-CH", lcid => 0x00000807, sabbrevlangname => "DES" },
|
|
{ name => "de-DE", lcid => 0x00000407 },
|
|
{ name => "de-DE_phoneb", lcid => 0x00010407, alias => "de-DE" },
|
|
{ name => "de-DE-u-co-phonebk", alias => "de-DE_phoneb" },
|
|
{ name => "de-IT", oemcp => 65001 },
|
|
{ name => "de-LI", lcid => 0x00001407, sabbrevlangname => "DEC" },
|
|
{ name => "de-LU", lcid => 0x00001007, sabbrevlangname => "DEL" },
|
|
{ name => "dje", sopentypelang => "DJR" },
|
|
{ name => "dje-NE" },
|
|
{ name => "doi", sopentypelang => "DGR" },
|
|
{ name => "doi-IN", alias => "doi-Deva-IN" },
|
|
{ name => "doi-Deva", file => "doi" },
|
|
{ name => "doi-Deva-IN", file => "doi_IN" },
|
|
{ name => "dsb", lcid => 0x00007c2e, sparent => "hsb", oemcp => 850, ebcdiccp => 870, sabbrevlangname => "DSB", sopentypelang => "LSB" },
|
|
{ name => "dsb-DE", lcid => 0x0000082e },
|
|
{ name => "dua" },
|
|
{ name => "dua-CM" },
|
|
{ name => "dv", lcid => 0x00000065, slist => "\x{060c}", group => 13, nativedigits => "0123456789" },
|
|
{ name => "dv-MV", lcid => 0x00000465 },
|
|
{ name => "dyo" },
|
|
{ name => "dyo-SN" },
|
|
{ name => "dz", sopentypelang => "DZN" },
|
|
{ name => "dz-BT", lcid => 0x00000c51, sabbrevlangname => "ZZZ" },
|
|
{ name => "ebu" },
|
|
{ name => "ebu-KE" },
|
|
{ name => "ee" },
|
|
{ name => "ee-GH" },
|
|
{ name => "ee-TG" },
|
|
{ name => "el", lcid => 0x00000008, oemcp => 737, group => 4 },
|
|
{ name => "el-CY" },
|
|
{ name => "el-GR", lcid => 0x00000408 },
|
|
{ name => "en", lcid => 0x00000009, oemcp => 437, slist => ",", sabbrevlangname => "ENU" },
|
|
{ name => "en-001", oemcp => 850 },
|
|
{ name => "en-029", lcid => 0x00002409, file => "en", oemcp => 850, sabbrevlangname => "ENB" },
|
|
{ name => "en-150", oemcp => 65001 },
|
|
{ name => "en-AE", lcid => 0x00004c09, oemcp => 65001, sabbrevlangname => "ZZZ" },
|
|
{ name => "en-AG", oemcp => 850 },
|
|
{ name => "en-AI", oemcp => 850 },
|
|
{ name => "en-AS", oemcp => 850 },
|
|
{ name => "en-AT", oemcp => 65001 },
|
|
{ name => "en-AU", lcid => 0x00000c09, oemcp => 850, sabbrevlangname => "ENA" },
|
|
{ name => "en-BB", oemcp => 850 },
|
|
{ name => "en-BE", oemcp => 850 },
|
|
{ name => "en-BI", oemcp => 65001 },
|
|
{ name => "en-BM", oemcp => 850 },
|
|
{ name => "en-BS", oemcp => 850 },
|
|
{ name => "en-BW", oemcp => 850 },
|
|
{ name => "en-BZ", lcid => 0x00002809, oemcp => 850, sabbrevlangname => "ENL" },
|
|
{ name => "en-CA", lcid => 0x00001009, oemcp => 850, ebcdiccp => 37, sabbrevlangname => "ENC" },
|
|
{ name => "en-CC", oemcp => 850 },
|
|
{ name => "en-CH", oemcp => 65001 },
|
|
{ name => "en-CK", oemcp => 850 },
|
|
{ name => "en-CM", oemcp => 850 },
|
|
{ name => "en-CX", oemcp => 850 },
|
|
{ name => "en-CY", oemcp => 65001 },
|
|
{ name => "en-DE", oemcp => 65001 },
|
|
{ name => "en-DG", oemcp => 850 },
|
|
{ name => "en-DK", oemcp => 65001 },
|
|
{ name => "en-DM", oemcp => 850 },
|
|
{ name => "en-ER", oemcp => 850 },
|
|
{ name => "en-FI", oemcp => 65001 },
|
|
{ name => "en-FJ", oemcp => 850 },
|
|
{ name => "en-FK", oemcp => 850 },
|
|
{ name => "en-FM", oemcp => 850 },
|
|
{ name => "en-GB", lcid => 0x00000809, oemcp => 850, ebcdiccp => 20285, sabbrevlangname => "ENG" },
|
|
{ name => "en-GD", oemcp => 850 },
|
|
{ name => "en-GG", oemcp => 850 },
|
|
{ name => "en-GH", oemcp => 850 },
|
|
{ name => "en-GI", oemcp => 850 },
|
|
{ name => "en-GM", oemcp => 850 },
|
|
{ name => "en-GU", oemcp => 850 },
|
|
{ name => "en-GY", oemcp => 850 },
|
|
{ name => "en-HK", lcid => 0x00003c09, oemcp => 850, sabbrevlangname => "ENH" },
|
|
{ name => "en-ID", lcid => 0x00003809, oemcp => 850, sabbrevlangname => "ZZZ" },
|
|
{ name => "en-IE", lcid => 0x00001809, oemcp => 850, sabbrevlangname => "ENI" },
|
|
{ name => "en-IL", oemcp => 65001 },
|
|
{ name => "en-IM", oemcp => 850 },
|
|
{ name => "en-IN", lcid => 0x00004009, sabbrevlangname => "ENN" },
|
|
{ name => "en-IO", oemcp => 850 },
|
|
{ name => "en-JE", oemcp => 850 },
|
|
{ name => "en-JM", lcid => 0x00002009, oemcp => 850, sabbrevlangname => "ENJ" },
|
|
{ name => "en-KE", oemcp => 850 },
|
|
{ name => "en-KI", oemcp => 850 },
|
|
{ name => "en-KN", oemcp => 850 },
|
|
{ name => "en-KY", oemcp => 850 },
|
|
{ name => "en-LC", oemcp => 850 },
|
|
{ name => "en-LR", oemcp => 850 },
|
|
{ name => "en-LS", oemcp => 850 },
|
|
{ name => "en-MG", oemcp => 850 },
|
|
{ name => "en-MH", oemcp => 850 },
|
|
{ name => "en-MO", oemcp => 850 },
|
|
{ name => "en-MP", oemcp => 850 },
|
|
{ name => "en-MS", oemcp => 850 },
|
|
{ name => "en-MT", oemcp => 850 },
|
|
{ name => "en-MU", oemcp => 850 },
|
|
{ name => "en-MW", oemcp => 850 },
|
|
{ name => "en-MY", lcid => 0x00004409, sabbrevlangname => "ENM" },
|
|
{ name => "en-NA", oemcp => 850 },
|
|
{ name => "en-NF", oemcp => 850 },
|
|
{ name => "en-NG", oemcp => 850 },
|
|
{ name => "en-NL", oemcp => 65001 },
|
|
{ name => "en-NR", oemcp => 850 },
|
|
{ name => "en-NU", oemcp => 850 },
|
|
{ name => "en-NZ", lcid => 0x00001409, oemcp => 850, sabbrevlangname => "ENZ" },
|
|
{ name => "en-PG", oemcp => 850 },
|
|
{ name => "en-PH", lcid => 0x00003409, ebcdiccp => 500, sabbrevlangname => "ENP" },
|
|
{ name => "en-PK", oemcp => 850 },
|
|
{ name => "en-PN", oemcp => 850 },
|
|
{ name => "en-PR", oemcp => 850 },
|
|
{ name => "en-PW", oemcp => 850 },
|
|
{ name => "en-RW", oemcp => 850 },
|
|
{ name => "en-SB", oemcp => 850 },
|
|
{ name => "en-SC", oemcp => 850 },
|
|
{ name => "en-SD", oemcp => 850 },
|
|
{ name => "en-SE", oemcp => 65001 },
|
|
{ name => "en-SG", lcid => 0x00004809, sabbrevlangname => "ENE" },
|
|
{ name => "en-SH", oemcp => 850 },
|
|
{ name => "en-SI", oemcp => 65001 },
|
|
{ name => "en-SL", oemcp => 850 },
|
|
{ name => "en-SS", oemcp => 850 },
|
|
{ name => "en-SX", oemcp => 850 },
|
|
{ name => "en-SZ", oemcp => 850 },
|
|
{ name => "en-TC", oemcp => 850 },
|
|
{ name => "en-TK", oemcp => 850 },
|
|
{ name => "en-TO", oemcp => 850 },
|
|
{ name => "en-TT", lcid => 0x00002c09, oemcp => 850, sabbrevlangname => "ENT" },
|
|
{ name => "en-TV", oemcp => 850 },
|
|
{ name => "en-TZ", oemcp => 850 },
|
|
{ name => "en-UG", oemcp => 850 },
|
|
{ name => "en-UM", oemcp => 850 },
|
|
{ name => "en-US", lcid => 0x00000409 },
|
|
{ name => "en-VC", oemcp => 850 },
|
|
{ name => "en-VG", oemcp => 850 },
|
|
{ name => "en-VI", oemcp => 850 },
|
|
{ name => "en-VU", oemcp => 850 },
|
|
{ name => "en-WS", oemcp => 850 },
|
|
{ name => "en-ZA", lcid => 0x00001c09, ebcdiccp => 500, sabbrevlangname => "ENS" },
|
|
{ name => "en-ZM", oemcp => 850 },
|
|
{ name => "en-ZW", lcid => 0x00003009, ebcdiccp => 500, sabbrevlangname => "ENW" },
|
|
{ name => "eo", sopentypelang => "NTO" },
|
|
{ name => "eo-001" },
|
|
{ name => "es", lcid => 0x0000000a, oemcp => 850, ebcdiccp => 20284, sabbrevlangname => "ESP", sopentypelang => "ESP" },
|
|
{ name => "es-419", lcid => 0x0000580a, sabbrevlangname => "ESJ" },
|
|
{ name => "es-AR", lcid => 0x00002c0a, sabbrevlangname => "ESS" },
|
|
{ name => "es-BO", lcid => 0x0000400a, sabbrevlangname => "ESB" },
|
|
{ name => "es-BR", oemcp => 65001 },
|
|
{ name => "es-BZ", oemcp => 65001 },
|
|
{ name => "es-CL", lcid => 0x0000340a, sabbrevlangname => "ESL" },
|
|
{ name => "es-CO", lcid => 0x0000240a, sabbrevlangname => "ESO" },
|
|
{ name => "es-CR", lcid => 0x0000140a, sabbrevlangname => "ESC" },
|
|
{ name => "es-CU", lcid => 0x00005c0a, sabbrevlangname => "ESK" },
|
|
{ name => "es-DO", lcid => 0x00001c0a, sabbrevlangname => "ESD" },
|
|
{ name => "es-EA" },
|
|
{ name => "es-EC", lcid => 0x0000300a, sabbrevlangname => "ESF" },
|
|
{ name => "es-ES", lcid => 0x00000c0a, sabbrevlangname => "ESN" },
|
|
{ name => "es-ES_tradnl", lcid => 0x0000040a, file => "es_ES" },
|
|
{ name => "es-ES-u-co-trad", alias => "es-ES_tradnl" },
|
|
{ name => "es-GQ" },
|
|
{ name => "es-GT", lcid => 0x0000100a, sabbrevlangname => "ESG" },
|
|
{ name => "es-HN", lcid => 0x0000480a, sabbrevlangname => "ESH" },
|
|
{ name => "es-IC" },
|
|
{ name => "es-MX", lcid => 0x0000080a, sabbrevlangname => "ESM" },
|
|
{ name => "es-NI", lcid => 0x00004c0a, sabbrevlangname => "ESI" },
|
|
{ name => "es-PA", lcid => 0x0000180a, sabbrevlangname => "ESA" },
|
|
{ name => "es-PE", lcid => 0x0000280a, sabbrevlangname => "ESR" },
|
|
{ name => "es-PH" },
|
|
{ name => "es-PR", lcid => 0x0000500a, sabbrevlangname => "ESU" },
|
|
{ name => "es-PY", lcid => 0x00003c0a, sabbrevlangname => "ESZ" },
|
|
{ name => "es-SV", lcid => 0x0000440a, sabbrevlangname => "ESE" },
|
|
{ name => "es-US", lcid => 0x0000540a, sabbrevlangname => "EST" },
|
|
{ name => "es-UY", lcid => 0x0000380a, sabbrevlangname => "ESY" },
|
|
{ name => "es-VE", lcid => 0x0000200a, sabbrevlangname => "ESV" },
|
|
{ name => "et", lcid => 0x00000025, oemcp => 775, group => 3, sabbrevlangname => "ETI", sopentypelang => "ETI" },
|
|
{ name => "et-EE", lcid => 0x00000425 },
|
|
{ name => "eu", lcid => 0x0000002d, oemcp => 850, maccp => 65001, sabbrevlangname => "EUQ", sopentypelang => "EUQ" },
|
|
{ name => "eu-ES", lcid => 0x0000042d },
|
|
{ name => "ewo" },
|
|
{ name => "ewo-CM" },
|
|
{ name => "fa", lcid => 0x00000029, inegnumber => 3, oemcp => 720, slist => "\x{061b}", group => 13, sabbrevlangname => "FAR", sopentypelang => "FAR" },
|
|
{ name => "fa-AF", alias => "prs-AF" },
|
|
{ name => "fa-IR", lcid => 0x00000429 },
|
|
{ name => "ff", lcid => 0x00000067, oemcp => 850, ebcdiccp => 20297 },
|
|
{ name => "ff-CM", alias => "ff-Latn-CM" },
|
|
{ name => "ff-GN", alias => "ff-Latn-GN" },
|
|
{ name => "ff-MR", alias => "ff-Latn-MR" },
|
|
{ name => "ff-NG", alias => "ff-Latn-NG" },
|
|
{ name => "ff-SN", alias => "ff-Latn-SN" },
|
|
{ name => "ff-Adlm", oemcp => 65001 },
|
|
{ name => "ff-Adlm-BF" },
|
|
{ name => "ff-Adlm-CM" },
|
|
{ name => "ff-Adlm-GH" },
|
|
{ name => "ff-Adlm-GM" },
|
|
{ name => "ff-Adlm-GN" },
|
|
{ name => "ff-Adlm-GW" },
|
|
{ name => "ff-Adlm-LR" },
|
|
{ name => "ff-Adlm-MR" },
|
|
{ name => "ff-Adlm-NE" },
|
|
{ name => "ff-Adlm-NG" },
|
|
{ name => "ff-Adlm-SL" },
|
|
{ name => "ff-Adlm-SN" },
|
|
{ name => "ff-Latn", lcid => 0x00007c67 },
|
|
{ name => "ff-Latn-BF", oemcp => 65001 },
|
|
{ name => "ff-Latn-CM" },
|
|
{ name => "ff-Latn-GH", oemcp => 65001 },
|
|
{ name => "ff-Latn-GM", oemcp => 65001 },
|
|
{ name => "ff-Latn-GN" },
|
|
{ name => "ff-Latn-GW", oemcp => 65001 },
|
|
{ name => "ff-Latn-LR", oemcp => 65001 },
|
|
{ name => "ff-Latn-MR" },
|
|
{ name => "ff-Latn-NE", oemcp => 65001 },
|
|
{ name => "ff-Latn-NG", lcid => 0x00000467, sabbrevlangname => "ZZZ" },
|
|
{ name => "ff-Latn-SL", oemcp => 65001 },
|
|
{ name => "ff-Latn-SN", lcid => 0x00000867 },
|
|
{ name => "fi", lcid => 0x0000000b, oemcp => 850, ebcdiccp => 20278 },
|
|
{ name => "fi-FI", lcid => 0x0000040b },
|
|
{ name => "fil", lcid => 0x00000064, oemcp => 437, ebcdiccp => 500, sabbrevlangname => "FPO", sopentypelang => "PIL" },
|
|
{ name => "fil-PH", lcid => 0x00000464 },
|
|
{ name => "fil-Latn", alias => "fil" },
|
|
{ name => "fil-Latn-PH", alias => "fil-PH" },
|
|
{ name => "fo", lcid => 0x00000038, oemcp => 850, maccp => 10079, ebcdiccp => 20277, sabbrevlangname => "FOS", sopentypelang => "FOS" },
|
|
{ name => "fo-DK", oemcp => 65001, maccp => 65001 },
|
|
{ name => "fo-FO", lcid => 0x00000438 },
|
|
{ name => "fr", lcid => 0x0000000c, oemcp => 850, ebcdiccp => 20297 },
|
|
{ name => "fr-029", lcid => 0x00001c0c, file => "fr", sabbrevlangname => "ZZZ" },
|
|
{ name => "fr-BE", lcid => 0x0000080c, sabbrevlangname => "FRB" },
|
|
{ name => "fr-BF" },
|
|
{ name => "fr-BI" },
|
|
{ name => "fr-BJ" },
|
|
{ name => "fr-BL" },
|
|
{ name => "fr-CA", lcid => 0x00000c0c, sabbrevlangname => "FRC" },
|
|
{ name => "fr-CD", lcid => 0x0000240c, sabbrevlangname => "FRD" },
|
|
{ name => "fr-CF" },
|
|
{ name => "fr-CG" },
|
|
{ name => "fr-CH", lcid => 0x0000100c, sabbrevlangname => "FRS" },
|
|
{ name => "fr-CI", lcid => 0x0000300c, sabbrevlangname => "FRI" },
|
|
{ name => "fr-CM", lcid => 0x00002c0c, sabbrevlangname => "FRE" },
|
|
{ name => "fr-DJ" },
|
|
{ name => "fr-DZ" },
|
|
{ name => "fr-FR", lcid => 0x0000040c },
|
|
{ name => "fr-GA" },
|
|
{ name => "fr-GF" },
|
|
{ name => "fr-GN" },
|
|
{ name => "fr-GP" },
|
|
{ name => "fr-GQ" },
|
|
{ name => "fr-HT", lcid => 0x00003c0c, sabbrevlangname => "FRH" },
|
|
{ name => "fr-KM" },
|
|
{ name => "fr-LU", lcid => 0x0000140c, sabbrevlangname => "FRL" },
|
|
{ name => "fr-MA", lcid => 0x0000380c, sabbrevlangname => "FRO" },
|
|
{ name => "fr-MC", lcid => 0x0000180c, sabbrevlangname => "FRM" },
|
|
{ name => "fr-MF" },
|
|
{ name => "fr-MG" },
|
|
{ name => "fr-ML", lcid => 0x0000340c, sabbrevlangname => "FRF" },
|
|
{ name => "fr-MQ" },
|
|
{ name => "fr-MR" },
|
|
{ name => "fr-MU" },
|
|
{ name => "fr-NC" },
|
|
{ name => "fr-NE" },
|
|
{ name => "fr-PF" },
|
|
{ name => "fr-PM" },
|
|
{ name => "fr-RE", lcid => 0x0000200c, sabbrevlangname => "FRR" },
|
|
{ name => "fr-RW" },
|
|
{ name => "fr-SC" },
|
|
{ name => "fr-SN", lcid => 0x0000280c, sabbrevlangname => "FRN" },
|
|
{ name => "fr-SY" },
|
|
{ name => "fr-TD" },
|
|
{ name => "fr-TG" },
|
|
{ name => "fr-TN" },
|
|
{ name => "fr-VU" },
|
|
{ name => "fr-WF" },
|
|
{ name => "fr-YT" },
|
|
{ name => "fur", sopentypelang => "FRL" },
|
|
{ name => "fur-IT" },
|
|
{ name => "fuv-NG", alias => "ff-Latn-NG" },
|
|
{ name => "fy", lcid => 0x00000062, oemcp => 850, sabbrevlangname => "FYN", sopentypelang => "FRI" },
|
|
{ name => "fy-NL", lcid => 0x00000462 },
|
|
{ name => "ga", lcid => 0x0000003c, oemcp => 850, sabbrevlangname => "IRE", sopentypelang => "IRI" },
|
|
{ name => "ga-GB", oemcp => 65001 },
|
|
{ name => "ga-IE", lcid => 0x0000083c },
|
|
{ name => "gd", lcid => 0x00000091, oemcp => 850, ebcdiccp => 20285, sopentypelang => "GAE" },
|
|
{ name => "gd-GB", lcid => 0x00000491 },
|
|
{ name => "gd-Latn", alias => "gd" },
|
|
{ name => "gl", lcid => 0x00000056, oemcp => 850, sabbrevlangname => "GLC", sopentypelang => "GAL" },
|
|
{ name => "gl-ES", lcid => 0x00000456 },
|
|
{ name => "gn", lcid => 0x00000074, oemcp => 850, ebcdiccp => 20284, slist => ",", sopentypelang => "GUA" },
|
|
{ name => "gn-PY", lcid => 0x00000474 },
|
|
{ name => "gsw", lcid => 0x00000084, oemcp => 850, ebcdiccp => 20297, sabbrevlangname => "ZZZ", sopentypelang => "ALS" },
|
|
{ name => "gsw-CH" },
|
|
{ name => "gsw-FR", lcid => 0x00000484, sabbrevlangname => "GSW" },
|
|
{ name => "gsw-LI" },
|
|
{ name => "gu", lcid => 0x00000047, slist => ",", group => 15 },
|
|
{ name => "gu-IN", lcid => 0x00000447 },
|
|
{ name => "guz" },
|
|
{ name => "guz-KE" },
|
|
{ name => "gv", sopentypelang => "MNX" },
|
|
{ name => "gv-GB", file => "gv" },
|
|
{ name => "gv-IM" },
|
|
{ name => "ha", lcid => 0x00000068, oemcp => 437 },
|
|
{ name => "ha-GH", alias => "ha-Latn-GH" },
|
|
{ name => "ha-Latn", lcid => 0x00007c68, file => "ha" },
|
|
{ name => "ha-Latn-GH", file => "ha_GH", ebcdiccp => 500 },
|
|
{ name => "ha-Latn-NE", file => "ha_NE", ebcdiccp => 500 },
|
|
{ name => "ha-Latn-NG", lcid => 0x00000468, file => "ha_NG" },
|
|
{ name => "ha-NE", alias => "ha-Latn-NE" },
|
|
{ name => "ha-NG", alias => "ha-Latn-NG" },
|
|
{ name => "haw", lcid => 0x00000075, oemcp => 437 },
|
|
{ name => "haw-Latn", alias => "haw" },
|
|
{ name => "haw-Latn-US", alias => "haw-US" },
|
|
{ name => "haw-US", lcid => 0x00000475 },
|
|
{ name => "he", lcid => 0x0000000d, oemcp => 862, slist => ",", group => 12, sopentypelang => "IWR" },
|
|
{ name => "he-IL", lcid => 0x0000040d },
|
|
{ name => "hi", lcid => 0x00000039, slist => ",", group => 15 },
|
|
{ name => "hi-IN", lcid => 0x00000439 },
|
|
{ name => "hr", lcid => 0x0000001a, inegnumber => 2, oemcp => 852, maccp => 10082, group => 2 },
|
|
{ name => "hr-BA", lcid => 0x0000101a, ebcdiccp => 870, inegnumber => 1, sabbrevlangname => "HRB" },
|
|
{ name => "hr-HR", lcid => 0x0000041a },
|
|
{ name => "hsb", lcid => 0x0000002e, oemcp => 850, ebcdiccp => 870, sopentypelang => "USB" },
|
|
{ name => "hsb-DE", lcid => 0x0000042e },
|
|
{ name => "hu", lcid => 0x0000000e, oemcp => 852, group => 2 },
|
|
{ name => "hu-HU", lcid => 0x0000040e },
|
|
{ name => "hu-HU_technl", lcid => 0x0001040e, alias => "hu-HU" },
|
|
{ name => "hy", lcid => 0x0000002b, slist => ",", group => 17 },
|
|
{ name => "hy-AM", lcid => 0x0000042b },
|
|
{ name => "ia" },
|
|
{ name => "ia-001" },
|
|
## name => "ibb", lcid => 0x00000069 },
|
|
## name => "ibb-NG", lcid => 0x00000469 },
|
|
{ name => "id", lcid => 0x00000021, oemcp => 850 },
|
|
{ name => "id-ID", lcid => 0x00000421 },
|
|
{ name => "ie" },
|
|
{ name => "ie-EE" },
|
|
{ name => "ig", lcid => 0x00000070, oemcp => 437 },
|
|
{ name => "ig-Latn", alias => "ig" },
|
|
{ name => "ig-Latn-NG", alias => "ig-NG" },
|
|
{ name => "ig-NG", lcid => 0x00000470 },
|
|
{ name => "ii", lcid => 0x00000078, group => 9, sopentypelang => "YIM" },
|
|
{ name => "ii-CN", lcid => 0x00000478 },
|
|
{ name => "ii-Yiii", alias => "ii" },
|
|
{ name => "ii-Yiii-CN", alias => "ii-CN" },
|
|
{ name => "is", lcid => 0x0000000f, oemcp => 850, maccp => 10079, ebcdiccp => 20871 },
|
|
{ name => "is-IS", lcid => 0x0000040f },
|
|
{ name => "it", lcid => 0x00000010, oemcp => 850, ebcdiccp => 20280 },
|
|
{ name => "it-CH", lcid => 0x00000810, ebcdiccp => 500, sabbrevlangname => "ITS" },
|
|
{ name => "it-IT", lcid => 0x00000410 },
|
|
{ name => "it-SM" },
|
|
{ name => "it-VA", oemcp => 65001 },
|
|
{ name => "iu", lcid => 0x0000005d, oemcp => 437, slist => ",", sortlocale => "iu-Latn-CA", sabbrevlangname => "IUK", sopentypelang => "INU" },
|
|
{ name => "iu-Cans", lcid => 0x0000785d, file => "iu", oemcp => 65001, sabbrevlangname => "IUS" },
|
|
{ name => "iu-Cans-CA", lcid => 0x0000045d, file => "iu_CA" },
|
|
{ name => "iu-Latn", lcid => 0x00007c5d },
|
|
{ name => "iu-Latn-CA", lcid => 0x0000085d },
|
|
{ name => "ja", lcid => 0x00000011, ireadinglayout => 2, oemcp => 932, slist => ",", sscripts => "Hani Hira Jpan Kana", group => 7, sopentypelang => "JAN" },
|
|
{ name => "ja-JP", lcid => 0x00000411 },
|
|
{ name => "ja-JP_radstr", lcid => 0x00040411, alias => "ja-JP" },
|
|
{ name => "ja-JP-u-co-unihan", alias => "ja-JP_radstr" },
|
|
{ name => "jgo" },
|
|
{ name => "jgo-CM" },
|
|
{ name => "jmc" },
|
|
{ name => "jmc-TZ" },
|
|
{ name => "jv", oemcp => 850, nativedigits => "0123456789" },
|
|
{ name => "jv-ID", alias => "jv-Latn-ID" },
|
|
## name => "jv-Java" },
|
|
## name => "jv-Java-ID" },
|
|
{ name => "jv-Latn", file => "jv" },
|
|
{ name => "jv-Latn-ID", file => "jv_ID" },
|
|
{ name => "ka", lcid => 0x00000037, group => 16 },
|
|
{ name => "ka-GE", lcid => 0x00000437 },
|
|
{ name => "ka-GE_modern", lcid => 0x00010437, alias => "ka-GE" },
|
|
{ name => "kab", sopentypelang => "KAB0" },
|
|
{ name => "kab-DZ" },
|
|
{ name => "kam", sopentypelang => "KMB" },
|
|
{ name => "kam-KE" },
|
|
{ name => "kde" },
|
|
{ name => "kde-TZ" },
|
|
{ name => "kea" },
|
|
{ name => "kea-CV" },
|
|
{ name => "kgp" },
|
|
{ name => "kgp-BR" },
|
|
{ name => "khq" },
|
|
{ name => "khq-ML" },
|
|
{ name => "ki" },
|
|
{ name => "ki-KE" },
|
|
{ name => "kk", lcid => 0x0000003f, group => 5, sabbrevlangname => "KKZ" },
|
|
{ name => "kk-Cyrl", alias => "kk" },
|
|
{ name => "kk-Cyrl-KZ", alias => "kk-KZ" },
|
|
{ name => "kk-KZ", lcid => 0x0000043f },
|
|
{ name => "kkj" },
|
|
{ name => "kkj-CM" },
|
|
{ name => "kl", lcid => 0x0000006f, oemcp => 850, ebcdiccp => 20277, sopentypelang => "GRN" },
|
|
{ name => "kl-GL", lcid => 0x0000046f },
|
|
{ name => "kln", sopentypelang => "KAL" },
|
|
{ name => "kln-KE" },
|
|
{ name => "km", lcid => 0x00000053, inegnumber => 2, slist => ",", group => 15 },
|
|
{ name => "km-KH", lcid => 0x00000453 },
|
|
{ name => "kn", lcid => 0x0000004b, slist => ",", group => 15, sabbrevlangname => "KDI" },
|
|
{ name => "kn-IN", lcid => 0x0000044b },
|
|
{ name => "ko", lcid => 0x00000012, ireadinglayout => 2, slist => ",", oemcp => 949, ebcdiccp => 20833, sscripts => "Hang Hani Kore", group => 8 },
|
|
{ name => "ko-KP", oemcp => 65001 },
|
|
{ name => "ko-KR", lcid => 0x00000412 },
|
|
{ name => "kok", lcid => 0x00000057, slist => ",", group => 15, sabbrevlangname => "KNK" },
|
|
{ name => "kok-IN", lcid => 0x00000457 },
|
|
{ name => "kr", lcid => 0x00000071, sortlocale => "kr-Latn-NG", oemcp => 850, dir => "exemplars", sabbrevlangname => "ZZZ", sopentypelang => "KNR" },
|
|
{ name => "kr-Latn", file => "kr", dir => "exemplars" },
|
|
{ name => "kr-Latn-NG", lcid => 0x00000471, file => "kr", dir => "exemplars" },
|
|
{ name => "kr-NG", alias => "kr-Latn-NG" },
|
|
{ name => "ks", lcid => 0x00000060, group => 15, sabbrevlangname => "ZZZ", sopentypelang => "KSH" },
|
|
{ name => "ks-Arab", lcid => 0x00000460 },
|
|
{ name => "ks-Arab-IN" },
|
|
{ name => "ks-Deva", slist => "," },
|
|
{ name => "ks-Deva-IN", lcid => 0x00000860 },
|
|
{ name => "ks-IN", alias => "ks-Arab-IN" },
|
|
{ name => "ksb" },
|
|
{ name => "ksb-TZ" },
|
|
{ name => "ksf" },
|
|
{ name => "ksf-CM" },
|
|
{ name => "ksh", sopentypelang => "KSH0" },
|
|
{ name => "ksh-DE" },
|
|
{ name => "ku", lcid => 0x00000092, file => "ckb", slist => "\x{061b}", sortlocale => "ku-Arab-IQ", oemcp => 720 },
|
|
{ name => "ku-Arab", lcid => 0x00007c92, file => "ckb", group => 13 },
|
|
{ name => "ku-Arab-IQ", lcid => 0x00000492, file => "ckb_IQ" },
|
|
{ name => "ku-Arab-IR", file => "ckb_IR", oemcp => 65001 },
|
|
{ name => "kw" },
|
|
{ name => "kw-GB" },
|
|
{ name => "ky", lcid => 0x00000040, oemcp => 866, group => 5, sabbrevlangname => "KYR" },
|
|
{ name => "ky-Cyrl", alias => "ky" },
|
|
{ name => "ky-Cyrl-KG", alias => "ky-KG" },
|
|
{ name => "ky-KG", lcid => 0x00000440 },
|
|
{ name => "la", lcid => 0x00000076, oemcp => 437, slist => ",", sabbrevlangname => "ZZZ" },
|
|
{ name => "la-VA", lcid => 0x00000476 },
|
|
{ name => "la-001", alias => "la-VA" },
|
|
{ name => "lag" },
|
|
{ name => "lag-TZ" },
|
|
{ name => "lb", lcid => 0x0000006e, oemcp => 850, ebcdiccp => 20297, sabbrevlangname => "LBX" },
|
|
{ name => "lb-LU", lcid => 0x0000046e },
|
|
{ name => "lg" },
|
|
{ name => "lg-UG" },
|
|
{ name => "lkt" },
|
|
{ name => "lkt-US" },
|
|
{ name => "ln" },
|
|
{ name => "ln-AO" },
|
|
{ name => "ln-CD" },
|
|
{ name => "ln-CF" },
|
|
{ name => "ln-CG" },
|
|
{ name => "lo", lcid => 0x00000054, group => 15 },
|
|
{ name => "lo-LA", lcid => 0x00000454 },
|
|
{ name => "lrc" },
|
|
{ name => "lrc-IQ" },
|
|
{ name => "lrc-IR" },
|
|
{ name => "lt", lcid => 0x00000027, oemcp => 775, group => 3, sabbrevlangname => "LTH", sopentypelang => "LTH" },
|
|
{ name => "lt-LT", lcid => 0x00000427 },
|
|
{ name => "lu" },
|
|
{ name => "lu-CD" },
|
|
{ name => "luo" },
|
|
{ name => "luo-KE" },
|
|
{ name => "luy", sopentypelang => "LUH" },
|
|
{ name => "luy-KE" },
|
|
{ name => "lv", lcid => 0x00000026, oemcp => 775, group => 3, sabbrevlangname => "LVI", sopentypelang => "LVI" },
|
|
{ name => "lv-LV", lcid => 0x00000426 },
|
|
{ name => "mai" },
|
|
{ name => "mai-IN" },
|
|
{ name => "mas" },
|
|
{ name => "mas-KE" },
|
|
{ name => "mas-TZ" },
|
|
{ name => "mer" },
|
|
{ name => "mer-KE" },
|
|
{ name => "mfe" },
|
|
{ name => "mfe-MU" },
|
|
{ name => "mg" },
|
|
{ name => "mg-MG" },
|
|
{ name => "mgh" },
|
|
{ name => "mgh-MZ" },
|
|
{ name => "mgo" },
|
|
{ name => "mgo-CM" },
|
|
{ name => "mi", lcid => 0x00000081, slist => "," },
|
|
{ name => "mi-Latn", alias => "mi" },
|
|
{ name => "mi-Latn-NZ", alias => "mi-NZ" },
|
|
{ name => "mi-NZ", lcid => 0x00000481 },
|
|
{ name => "mic" },
|
|
{ name => "mic-CA" },
|
|
{ name => "mk", lcid => 0x0000002f, oemcp => 866, ebcdiccp => 500, group => 5, sabbrevlangname => "MKI" },
|
|
{ name => "mk-MK", lcid => 0x0000042f },
|
|
{ name => "ml", lcid => 0x0000004c, group => 15, sabbrevlangname => "MYM", sopentypelang => "MLR" },
|
|
{ name => "ml-IN", lcid => 0x0000044c },
|
|
{ name => "mn", lcid => 0x00000050, oemcp => 866, sopentypelang => "MNG" },
|
|
{ name => "mn-Cyrl", lcid => 0x00007850, file => "mn", sabbrevlangname => "MNN" },
|
|
{ name => "mn-Cyrl-MN", alias => "mn-MN" },
|
|
{ name => "mn-MN", lcid => 0x00000450, sparent => "mn-Cyrl", group => 5 },
|
|
{ name => "mn-Mong", lcid => 0x00007c50, oemcp => 65001, slist => ",", group => 15, sabbrevlangname => "MNG", nativedigits => "0123456789" },
|
|
{ name => "mn-Mong-CN", lcid => 0x00000850 },
|
|
{ name => "mn-Mong-MN", lcid => 0x00000c50, sabbrevlangname => "MNM" },
|
|
{ name => "mni", lcid => 0x00000058, slist => ",", sabbrevlangname => "ZZZ" },
|
|
{ name => "mni-IN", lcid => 0x00000458, file => "mni_Beng_IN" },
|
|
{ name => "mni-Beng" },
|
|
{ name => "mni-Beng-IN", alias => "mni-IN" },
|
|
{ name => "moh", lcid => 0x0000007c, oemcp => 850, ebcdiccp => 37, slist => ",", sabbrevlangname => "MWK" },
|
|
{ name => "moh-CA", lcid => 0x0000047c },
|
|
{ name => "moh-Latn", alias => "moh" },
|
|
{ name => "moh-Latn-CA", alias => "moh-CA" },
|
|
{ name => "mr", lcid => 0x0000004e, slist => ",", group => 15 },
|
|
{ name => "mr-IN", lcid => 0x0000044e },
|
|
{ name => "ms", lcid => 0x0000003e, oemcp => 850, sabbrevlangname => "MSL", sopentypelang => "MLY" },
|
|
{ name => "ms-BN", lcid => 0x0000083e, sabbrevlangname => "MSB" },
|
|
{ name => "ms-ID" },
|
|
{ name => "ms-Latn", alias => "ms" },
|
|
{ name => "ms-Latn-BN", alias => "ms-BN" },
|
|
{ name => "ms-Latn-MY", alias => "ms-MY" },
|
|
{ name => "ms-Latn-SG", alias => "ms-SG" },
|
|
{ name => "ms-MY", lcid => 0x0000043e },
|
|
{ name => "ms-SG" },
|
|
{ name => "mt", lcid => 0x0000003a, sopentypelang => "MTS" },
|
|
{ name => "mt-MT", lcid => 0x0000043a },
|
|
{ name => "mua" },
|
|
{ name => "mua-CM" },
|
|
{ name => "my", lcid => 0x00000055, sopentypelang => "BRM" },
|
|
{ name => "my-MM", lcid => 0x00000455 },
|
|
{ name => "mzn" },
|
|
{ name => "mzn-IR" },
|
|
{ name => "naq" },
|
|
{ name => "naq-NA" },
|
|
{ name => "nb", lcid => 0x00007c14, oemcp => 850, ebcdiccp => 20277, sabbrevlangname => "NOR", sopentypelang => "NOR" },
|
|
{ name => "nb-NO", lcid => 0x00000414 },
|
|
{ name => "nb-SJ" },
|
|
{ name => "nd", sopentypelang => "NDB" },
|
|
{ name => "nd-ZW" },
|
|
{ name => "nds" },
|
|
{ name => "nds-DE" },
|
|
{ name => "nds-NL" },
|
|
{ name => "ne", lcid => 0x00000061, slist => "," },
|
|
{ name => "ne-IN", lcid => 0x00000861, sabbrevlangname => "NEI" },
|
|
{ name => "ne-NP", lcid => 0x00000461, group => 15 },
|
|
{ name => "nl", lcid => 0x00000013, oemcp => 850 },
|
|
{ name => "nl-AW" },
|
|
{ name => "nl-BE", lcid => 0x00000813, sabbrevlangname => "NLB" },
|
|
{ name => "nl-BQ" },
|
|
{ name => "nl-CW" },
|
|
{ name => "nl-NL", lcid => 0x00000413 },
|
|
{ name => "nl-SR" },
|
|
{ name => "nl-SX" },
|
|
{ name => "nmg" },
|
|
{ name => "nmg-CM" },
|
|
{ name => "nn", lcid => 0x00007814, oemcp => 850, ebcdiccp => 20277, sabbrevlangname => "NON", sopentypelang => "NYN" },
|
|
{ name => "nn-NO", lcid => 0x00000814 },
|
|
{ name => "nnh" },
|
|
{ name => "nnh-CM" },
|
|
{ name => "no", lcid => 0x00000014, oemcp => 850, ebcdiccp => 20277, sortlocale => "nb-NO" },
|
|
{ name => "nqo", idigits => 3, inegnumber => 3, slist => "\x{060c}", sopentypelang => "NKO" },
|
|
{ name => "nqo-GN" },
|
|
{ name => "nr", sopentypelang => "NDB" },
|
|
{ name => "nr-ZA" },
|
|
{ name => "nso", lcid => 0x0000006c, oemcp => 850, sopentypelang => "SOT" },
|
|
{ name => "nso-ZA", lcid => 0x0000046c },
|
|
{ name => "nus" },
|
|
{ name => "nus-SD", alias => "nus-SS" },
|
|
{ name => "nus-SS" },
|
|
{ name => "nyn", sopentypelang => "NKL" },
|
|
{ name => "nyn-UG" },
|
|
{ name => "oc", lcid => 0x00000082, oemcp => 850, ebcdiccp => 20297 },
|
|
{ name => "oc-FR", lcid => 0x00000482 },
|
|
{ name => "oc-Latn", alias => "oc" },
|
|
{ name => "oc-Latn-FR", alias => "oc-FR" },
|
|
{ name => "om", lcid => 0x00000072, sopentypelang => "ORO" },
|
|
{ name => "om-ET", lcid => 0x00000472 },
|
|
{ name => "om-KE" },
|
|
{ name => "or", lcid => 0x00000048, slist => ",", group => 15 },
|
|
{ name => "or-IN", lcid => 0x00000448 },
|
|
{ name => "os" },
|
|
{ name => "os-GE" },
|
|
{ name => "os-RU" },
|
|
{ name => "pa", lcid => 0x00000046, slist => "," },
|
|
{ name => "pa-Arab", lcid => 0x00007c46, slist => ";", inegnumber => 2, oemcp => 720, group => 13, sabbrevlangname => "PAP" },
|
|
{ name => "pa-Arab-PK", lcid => 0x00000846 },
|
|
{ name => "pa-Guru" },
|
|
{ name => "pa-Guru-IN", alias => "pa-IN" },
|
|
{ name => "pa-IN", lcid => 0x00000446, sparent => "pa-Guru", file => "pa_Guru_IN", group => 15 },
|
|
{ name => "pap", lcid => 0x00000079, oemcp => 850, sopentypelang => "PAP0" },
|
|
## name => "pap-029", lcid => 0x00000479 },
|
|
{ name => "pcm" },
|
|
{ name => "pcm-NG", alias => "pcm-Latn-NG" },
|
|
{ name => "pcm-Latn", file => "pcm" },
|
|
{ name => "pcm-Latn-NG", file => "pcm_NG" },
|
|
{ name => "pl", lcid => 0x00000015, oemcp => 852, ebcdiccp => 20880, group => 2, sabbrevlangname => "PLK", sopentypelang => "PLK" },
|
|
{ name => "pl-PL", lcid => 0x00000415 },
|
|
{ name => "prg" },
|
|
{ name => "prg-001", file => "prg" },
|
|
{ name => "prg-PL" },
|
|
{ name => "prs", lcid => 0x0000008c, file => "fa", inegnumber => 3, oemcp => 720, group => 13, sopentypelang => "DRI" },
|
|
{ name => "prs-AF", lcid => 0x0000048c, file => "fa_AF" },
|
|
{ name => "prs-Arab", alias => "prs" },
|
|
{ name => "prs-Arab-AF", alias => "prs-AF" },
|
|
{ name => "ps", lcid => 0x00000063, group => 13, sabbrevlangname => "PAS", sopentypelang => "PAS" },
|
|
{ name => "ps-AF", lcid => 0x00000463 },
|
|
{ name => "ps-PK" },
|
|
{ name => "pt", lcid => 0x00000016, oemcp => 850, sabbrevlangname => "PTB", sopentypelang => "PTG" },
|
|
{ name => "pt-AO" },
|
|
{ name => "pt-BR", lcid => 0x00000416 },
|
|
{ name => "pt-CH", oemcp => 65001 },
|
|
{ name => "pt-CV" },
|
|
{ name => "pt-GQ", oemcp => 65001 },
|
|
{ name => "pt-GW" },
|
|
{ name => "pt-LU", oemcp => 65001 },
|
|
{ name => "pt-MO" },
|
|
{ name => "pt-MZ" },
|
|
{ name => "pt-PT", lcid => 0x00000816, sabbrevlangname => "PTG" },
|
|
{ name => "pt-ST" },
|
|
{ name => "pt-TL" },
|
|
## name => qps-Latn-x-sh", lcid => 0x80000901 },
|
|
## name => qps-ploc", lcid => 0x80000501 },
|
|
## name => qps-ploca", lcid => 0x800005fe },
|
|
## name => qps-plocm", lcid => 0x800009ff },
|
|
{ name => "qu", alias => "quz" },
|
|
{ name => "qu-BO", alias => "quz-BO" },
|
|
{ name => "qu-EC", alias => "quz-EC" },
|
|
{ name => "qu-PE", alias => "quz-PE" },
|
|
{ name => "quc", lcid => 0x00000086, oemcp => 850, ebcdiccp => 20284, slist => "," },
|
|
{ name => "quc-Latn", lcid => 0x00007c86, file => "quc" },
|
|
{ name => "quc-Latn-GT", lcid => 0x00000486, file => "quc_GT" },
|
|
{ name => "qut", alias => "quc" },
|
|
{ name => "qut-GT", alias => "quc-Latn-GT" },
|
|
{ name => "quz", lcid => 0x0000006b, file => "qu", territory => "BO", oemcp => 850, ebcdiccp => 20284, slist => "," },
|
|
{ name => "quz-BO", lcid => 0x0000046b, file => "qu_BO" },
|
|
{ name => "quz-EC", lcid => 0x0000086b, file => "qu_EC" },
|
|
{ name => "quz-Latn", alias => "quz" },
|
|
{ name => "quz-Latn-BO", alias => "quz-BO" },
|
|
{ name => "quz-Latn-EC", alias => "quz-EC" },
|
|
{ name => "quz-Latn-PE", alias => "quz-PE" },
|
|
{ name => "quz-PE", lcid => 0x00000c6b, file => "qu_PE" },
|
|
{ name => "rm", lcid => 0x00000017, oemcp => 850, ebcdiccp => 20273, sabbrevlangname => "RMC", sopentypelang => "RMS" },
|
|
{ name => "rm-CH", lcid => 0x00000417 },
|
|
{ name => "rn" },
|
|
{ name => "rn-BI" },
|
|
{ name => "ro", lcid => 0x00000018, oemcp => 852, ebcdiccp => 20880, sabbrevlangname => "ROM", sopentypelang => "ROM" },
|
|
{ name => "ro-MD", lcid => 0x00000818, maccp => 65001, sabbrevlangname => "ROD" },
|
|
{ name => "ro-RO", lcid => 0x00000418, group => 2 },
|
|
{ name => "rof" },
|
|
{ name => "rof-TZ" },
|
|
{ name => "ru", lcid => 0x00000019, oemcp => 866 },
|
|
{ name => "ru-BY", maccp => 65001 },
|
|
{ name => "ru-KG", maccp => 65001 },
|
|
{ name => "ru-KZ", maccp => 65001 },
|
|
{ name => "ru-MD", lcid => 0x00000819, maccp => 65001, sabbrevlangname => "RUM" },
|
|
{ name => "ru-RU", lcid => 0x00000419, group => 5 },
|
|
{ name => "ru-UA", maccp => 65001 },
|
|
{ name => "rw", lcid => 0x00000087, oemcp => 437, sopentypelang => "RUA" },
|
|
{ name => "rw-RW", lcid => 0x00000487 },
|
|
{ name => "rwk" },
|
|
{ name => "rwk-TZ" },
|
|
{ name => "sa", lcid => 0x0000004f, slist => ",", group => 15 },
|
|
{ name => "sa-Deva", alias => "sa" },
|
|
{ name => "sa-Deva-IN", alias => "sa-IN" },
|
|
{ name => "sa-IN", lcid => 0x0000044f },
|
|
{ name => "sah", lcid => 0x00000085, oemcp => 866, group => 5, sopentypelang => "YAK" },
|
|
{ name => "sah-Cyrl", alias => "sah" },
|
|
{ name => "sah-Cyrl-RU", alias => "sah-RU" },
|
|
{ name => "sah-RU", lcid => 0x00000485 },
|
|
{ name => "saq" },
|
|
{ name => "saq-KE" },
|
|
{ name => "sat" },
|
|
{ name => "sat-Olck" },
|
|
{ name => "sat-Olck-IN" },
|
|
{ name => "sbp" },
|
|
{ name => "sbp-TZ" },
|
|
{ name => "sc" },
|
|
{ name => "sc-IT" },
|
|
{ name => "sd", lcid => 0x00000059, inegnumber => 3, oemcp => 720, sabbrevlangname => "SIP" },
|
|
{ name => "sd-Arab", lcid => 0x00007c59, group => 13 },
|
|
{ name => "sd-Arab-PK", lcid => 0x00000859 },
|
|
{ name => "sd-Deva", inegnumber => 1, slist => ",", oemcp => 65001, group => 15 },
|
|
{ name => "sd-Deva-IN", lcid => 0x00000459, sabbrevlangname => "ZZZ" },
|
|
{ name => "sd-PK", alias => "sd-Arab-PK" },
|
|
{ name => "se", lcid => 0x0000003b, oemcp => 850, ebcdiccp => 20277, sopentypelang => "NSM" },
|
|
{ name => "se-FI", lcid => 0x00000c3b, ebcdiccp => 20278, sabbrevlangname => "SMG" },
|
|
{ name => "se-NO", lcid => 0x0000043b },
|
|
{ name => "se-SE", lcid => 0x0000083b, ebcdiccp => 20278, sabbrevlangname => "SMF" },
|
|
{ name => "se-Latn", alias => "se" },
|
|
{ name => "se-Latn-FI", alias => "se-FI" },
|
|
{ name => "se-Latn-NO", alias => "se-NO" },
|
|
{ name => "se-Latn-SE", alias => "se-SE" },
|
|
{ name => "seh" },
|
|
{ name => "seh-MZ" },
|
|
{ name => "ses" },
|
|
{ name => "ses-ML" },
|
|
{ name => "sg", sopentypelang => "SGO" },
|
|
{ name => "sg-CF" },
|
|
{ name => "shi" },
|
|
{ name => "shi-Latn" },
|
|
{ name => "shi-Latn-MA" },
|
|
{ name => "shi-Tfng" },
|
|
{ name => "shi-Tfng-MA" },
|
|
{ name => "si", lcid => 0x0000005b, group => 15, sopentypelang => "SNH" },
|
|
{ name => "si-LK", lcid => 0x0000045b },
|
|
{ name => "sk", lcid => 0x0000001b, oemcp => 852, ebcdiccp => 20880, group => 2, sabbrevlangname => "SKY", sopentypelang => "SKY" },
|
|
{ name => "sk-SK", lcid => 0x0000041b },
|
|
{ name => "skr" },
|
|
{ name => "skr-PK" },
|
|
{ name => "sl", lcid => 0x00000024, oemcp => 852, ebcdiccp => 20880, group => 2 },
|
|
{ name => "sl-SI", lcid => 0x00000424 },
|
|
{ name => "sma", lcid => 0x0000783b, sparent => "se", ebcdiccp => 20278, sabbrevlangname => "SMB", sopentypelang => "SSM" },
|
|
{ name => "sma-Latn", alias => "sma" },
|
|
{ name => "sma-Latn-NO", alias => "sma-NO" },
|
|
{ name => "sma-Latn-SE", alias => "sma-SE" },
|
|
{ name => "sma-NO", lcid => 0x0000183b, ebcdiccp => 20277, sabbrevlangname => "SMA" },
|
|
{ name => "sma-SE", lcid => 0x00001c3b },
|
|
{ name => "smj", lcid => 0x00007c3b, sparent => "se", ebcdiccp => 20278, sabbrevlangname => "SMK", sopentypelang => "LSM" },
|
|
{ name => "smj-Latn", alias => "smj" },
|
|
{ name => "smj-Latn-NO", alias => "smj-NO" },
|
|
{ name => "smj-Latn-SE", alias => "smj-SE" },
|
|
{ name => "smj-NO", lcid => 0x0000103b, ebcdiccp => 20277, sabbrevlangname => "SMJ" },
|
|
{ name => "smj-SE", lcid => 0x0000143b },
|
|
{ name => "smn", lcid => 0x0000703b, sparent => "se", ebcdiccp => 20278, sopentypelang => "ISM" },
|
|
{ name => "smn-FI", lcid => 0x0000243b },
|
|
{ name => "smn-Latn", alias => "smn" },
|
|
{ name => "smn-Latn-FI", alias => "smn-FI" },
|
|
{ name => "sms", lcid => 0x0000743b, sparent => "se", ebcdiccp => 20278, sopentypelang => "SKS" },
|
|
{ name => "sms-FI", lcid => 0x0000203b },
|
|
{ name => "sms-Latn", alias => "sms" },
|
|
{ name => "sms-Latn-FI", alias => "sms-FI" },
|
|
{ name => "sn", sopentypelang => "SNA0" },
|
|
{ name => "sn-Latn", file => "sn" },
|
|
{ name => "sn-Latn-ZW", file => "sn_ZW" },
|
|
{ name => "sn-ZW", alias => "sn-Latn-ZW" },
|
|
{ name => "so", lcid => 0x00000077, sopentypelang => "SML" },
|
|
{ name => "so-DJ" },
|
|
{ name => "so-ET" },
|
|
{ name => "so-KE" },
|
|
{ name => "so-SO", lcid => 0x00000477 },
|
|
{ name => "sq", lcid => 0x0000001c, oemcp => 852, ebcdiccp => 20880, group => 2 },
|
|
{ name => "sq-AL", lcid => 0x0000041c },
|
|
{ name => "sq-MK" },
|
|
{ name => "sq-XK" },
|
|
{ name => "sr", lcid => 0x00007c1a, sortlocale => "sr-Latn-RS", oemcp => 852, group => 2, sabbrevlangname => "SRB", sopentypelang => "SRB" },
|
|
{ name => "sr-Cyrl", lcid => 0x00006c1a, oemcp => 855, ebcdiccp => 21025, group => 5, sabbrevlangname => "SRO" },
|
|
{ name => "sr-Cyrl-BA", lcid => 0x00001c1a, sabbrevlangname => "SRN" },
|
|
{ name => "sr-Cyrl-ME", lcid => 0x0000301a, sabbrevlangname => "SRQ" },
|
|
{ name => "sr-Cyrl-RS", lcid => 0x0000281a },
|
|
{ name => "sr-Cyrl-XK" },
|
|
{ name => "sr-Latn", lcid => 0x0000701a, sabbrevlangname => "SRM" },
|
|
{ name => "sr-Latn-BA", lcid => 0x0000181a, maccp => 10082, ebcdiccp => 870, sabbrevlangname => "SRS" },
|
|
{ name => "sr-Latn-ME", lcid => 0x00002c1a, sabbrevlangname => "SRP" },
|
|
{ name => "sr-Latn-RS", lcid => 0x0000241a, sabbrevlangname => "SRM" },
|
|
{ name => "sr-Latn-XK" },
|
|
## name => "sr-Cyrl-CS", lcid => 0x00000c1a },
|
|
## name => "sr-Latn-CS", lcid => 0x0000081a },
|
|
{ name => "ss", sopentypelang => "SWZ" },
|
|
{ name => "ss-SZ" },
|
|
{ name => "ss-ZA" },
|
|
{ name => "ssy" },
|
|
{ name => "ssy-ER" },
|
|
{ name => "st", lcid => 0x00000030 },
|
|
{ name => "st-LS" },
|
|
{ name => "st-ZA", lcid => 0x00000430 },
|
|
{ name => "su" },
|
|
{ name => "su-Latn" },
|
|
{ name => "su-Latn-ID" },
|
|
{ name => "sv", lcid => 0x0000001d, oemcp => 850, ebcdiccp => 20278, sabbrevlangname => "SVE", sopentypelang => "SVE" },
|
|
{ name => "sv-AX" },
|
|
{ name => "sv-FI", lcid => 0x0000081d, sabbrevlangname => "SVF" },
|
|
{ name => "sv-SE", lcid => 0x0000041d, sabbrevlangname => "SVE" },
|
|
{ name => "sw", lcid => 0x00000041, territory => "KE", oemcp => 437, ebcdiccp => 500, sabbrevlangname => "SWK", sopentypelang => "SWK" },
|
|
{ name => "sw-CD" },
|
|
{ name => "sw-KE", lcid => 0x00000441 },
|
|
{ name => "sw-TZ" },
|
|
{ name => "sw-UG" },
|
|
{ name => "swc-CD", alias => "sw-CD" },
|
|
{ name => "syr", lcid => 0x0000005a, slist => ",", group => 13 },
|
|
{ name => "syr-SY", lcid => 0x0000045a },
|
|
{ name => "syr-Syrc", alias => "syr" },
|
|
{ name => "syr-Syrc-SY", alias => "syr-SY" },
|
|
{ name => "ta", lcid => 0x00000049, slist => ",", group => 15, sabbrevlangname => "TAI" },
|
|
{ name => "ta-IN", lcid => 0x00000449 },
|
|
{ name => "ta-LK", lcid => 0x00000849, sabbrevlangname => "TAM" },
|
|
{ name => "ta-MY" },
|
|
{ name => "ta-SG" },
|
|
{ name => "te", lcid => 0x0000004a, group => 15 },
|
|
{ name => "te-IN", lcid => 0x0000044a },
|
|
{ name => "teo" },
|
|
{ name => "teo-KE" },
|
|
{ name => "teo-UG" },
|
|
{ name => "tg", lcid => 0x00000028, oemcp => 866, group => 5, sabbrevlangname => "TAJ", sopentypelang => "TAJ" },
|
|
{ name => "tg-Cyrl", lcid => 0x00007c28, file => "tg" },
|
|
{ name => "tg-Cyrl-TJ", lcid => 0x00000428, file => "tg_TJ" },
|
|
{ name => "tg-TJ", alias => "tg-Cyrl-TJ" },
|
|
{ name => "th", lcid => 0x0000001e, oemcp => 874, ebcdiccp => 20838, slist => ",", group => 11 },
|
|
{ name => "th-TH", lcid => 0x0000041e },
|
|
{ name => "ti", lcid => 0x00000073, territory => "ER", sopentypelang => "TGY" },
|
|
{ name => "ti-ER", lcid => 0x00000873 },
|
|
{ name => "ti-ET", lcid => 0x00000473, sabbrevlangname => "TIE" },
|
|
{ name => "tig", sopentypelang => "TGR" },
|
|
{ name => "tig-ER" },
|
|
{ name => "tig-Ethi-ER", alias => "tig-ER" },
|
|
{ name => "tk", lcid => 0x00000042, oemcp => 852, ebcdiccp => 20880, group => 2, sopentypelang => "TKM" },
|
|
{ name => "tk-Latn", alias => "tk" },
|
|
{ name => "tk-Latn-TM", alias => "tk-TM" },
|
|
{ name => "tk-TM", lcid => 0x00000442 },
|
|
{ name => "tn", lcid => 0x00000032, oemcp => 850, sopentypelang => "TNA" },
|
|
{ name => "tn-BW", lcid => 0x00000832, sabbrevlangname => "TSB" },
|
|
{ name => "tn-ZA", lcid => 0x00000432 },
|
|
{ name => "to", sopentypelang => "TGN" },
|
|
{ name => "to-TO" },
|
|
{ name => "tr", lcid => 0x0000001f, oemcp => 857, ebcdiccp => 20905, group => 6, sabbrevlangname => "TRK", sopentypelang => "TRK" },
|
|
{ name => "tr-CY" },
|
|
{ name => "tr-TR", lcid => 0x0000041f },
|
|
{ name => "ts", lcid => 0x00000031, sopentypelang => "TSG" },
|
|
{ name => "ts-ZA", lcid => 0x00000431 },
|
|
{ name => "tt", lcid => 0x00000044, oemcp => 866, group => 5, sabbrevlangname => "TTT" },
|
|
{ name => "tt-Cyrl", alias => "tt" },
|
|
{ name => "tt-Cyrl-RU", alias => "tt-RU" },
|
|
{ name => "tt-RU", lcid => 0x00000444 },
|
|
{ name => "twq" },
|
|
{ name => "twq-NE" },
|
|
{ name => "tyv" },
|
|
{ name => "tyv-RU" },
|
|
{ name => "tzm", lcid => 0x0000005f, sortlocale => "tzm-Latn-DZ", oemcp => 850, ebcdiccp => 20297, sabbrevlangname => "TZA" },
|
|
{ name => "tzm-Latn", lcid => 0x00007c5f, territory => "DZ", file => "tzm" },
|
|
{ name => "tzm-Latn-MA", file => "tzm_MA", oemcp => 65001 },
|
|
{ name => "tzm-Latn-DZ", lcid => 0x0000085f, file => "tzm" },
|
|
{ name => "tzm-MA", alias => "tzm-Latn-MA" },
|
|
{ name => "tzm-DZ", alias => "tzm-Latn-DZ" },
|
|
## name => "tzm-Arab", group => 13 },
|
|
## name => "tzm-Arab-MA", lcid => 0x0000045f },
|
|
## name => "tzm-Tfng", lcid => 0x0000785f },
|
|
## name => "tzm-Tfng-MA", lcid => 0x0000105f },
|
|
{ name => "ug", lcid => 0x00000080, oemcp => 720, slist => ",", group => 13, sopentypelang => "UYG", nativedigits => "0123456789" },
|
|
{ name => "ug-Arab", alias => "ug" },
|
|
{ name => "ug-Arab-CN", alias => "ug-CN" },
|
|
{ name => "ug-CN", lcid => 0x00000480 },
|
|
{ name => "uk", lcid => 0x00000022, oemcp => 866, maccp => 10017, ebcdiccp => 500, group => 5 },
|
|
{ name => "uk-UA", lcid => 0x00000422 },
|
|
{ name => "ur", lcid => 0x00000020, oemcp => 720 },
|
|
{ name => "ur-IN", lcid => 0x00000820, maccp => 65001, sabbrevlangname => "URI" },
|
|
{ name => "ur-PK", lcid => 0x00000420, group => 13 },
|
|
{ name => "uz", lcid => 0x00000043, oemcp => 857, maccp => 10029, group => 2 },
|
|
{ name => "uz-Arab", oemcp => 65001, maccp => 65001 },
|
|
{ name => "uz-Arab-AF" },
|
|
{ name => "uz-Cyrl", lcid => 0x00007843, oemcp => 866, maccp => 10007, group => 5, sabbrevlangname => "UZC" },
|
|
{ name => "uz-Cyrl-UZ", lcid => 0x00000843 },
|
|
{ name => "uz-Latn", lcid => 0x00007c43 },
|
|
{ name => "uz-Latn-UZ", lcid => 0x00000443 },
|
|
{ name => "vai" },
|
|
{ name => "vai-Latn" },
|
|
{ name => "vai-Latn-LR" },
|
|
{ name => "vai-Vaii" },
|
|
{ name => "vai-Vaii-LR" },
|
|
{ name => "ve", lcid => 0x00000033, sabbrevlangname => "ZZZ" },
|
|
{ name => "ve-ZA", lcid => 0x00000433 },
|
|
{ name => "vi", lcid => 0x0000002a, oemcp => 1258, slist => ",", group => 14, sabbrevlangname => "VIT", sopentypelang => "VIT" },
|
|
{ name => "vi-VN", lcid => 0x0000042a },
|
|
{ name => "vmw" },
|
|
{ name => "vmw-MZ" },
|
|
{ name => "vo" },
|
|
{ name => "vo-001" },
|
|
{ name => "vun" },
|
|
{ name => "vun-TZ" },
|
|
{ name => "wa", oemcp => 850 },
|
|
{ name => "wa-BE" },
|
|
{ name => "wae" },
|
|
{ name => "wae-CH" },
|
|
{ name => "wal" },
|
|
{ name => "wal-ET" },
|
|
{ name => "wo", lcid => 0x00000088, oemcp => 850, ebcdiccp => 20297, sopentypelang => "WLF" },
|
|
{ name => "wo-Latn", alias => "wo" },
|
|
{ name => "wo-Latn-SN", alias => "wo-SN" },
|
|
{ name => "wo-SN", lcid => 0x00000488 },
|
|
{ name => "x-IV_mathan", lcid => 0x0001007f, alias => "" },
|
|
{ name => "xh", lcid => 0x00000034, oemcp => 850, sopentypelang => "XHS" },
|
|
{ name => "xh-ZA", lcid => 0x00000434 },
|
|
{ name => "xnr" },
|
|
{ name => "xnr-IN" },
|
|
{ name => "xog" },
|
|
{ name => "xog-UG" },
|
|
{ name => "yav" },
|
|
{ name => "yav-CM" },
|
|
{ name => "yi", lcid => 0x0000003d, sabbrevlangname => "ZZZ", sopentypelang => "JII" },
|
|
{ name => "yi-001", lcid => 0x0000043d, file => "yi" },
|
|
{ name => "yi-UA" },
|
|
{ name => "yo", lcid => 0x0000006a, oemcp => 437, sopentypelang => "YBA" },
|
|
{ name => "yo-BJ", ebcdiccp => 500 },
|
|
{ name => "yo-Latn", alias => "yo" },
|
|
{ name => "yo-Latn-NG", alias => "yo-NG" },
|
|
{ name => "yo-NG", lcid => 0x0000046a },
|
|
{ name => "yrl" },
|
|
{ name => "yrl-BR" },
|
|
{ name => "yrl-CO" },
|
|
{ name => "yrl-VE" },
|
|
{ name => "yue" },
|
|
{ name => "yue-Hans" },
|
|
{ name => "yue-Hans-CN" },
|
|
{ name => "yue-Hant" },
|
|
{ name => "yue-Hant-HK" },
|
|
{ name => "zgh" },
|
|
{ name => "zgh-MA", alias => "zgh-Tfng-MA" },
|
|
{ name => "zgh-Tfng", file => "zgh" },
|
|
{ name => "zgh-Tfng-MA", file => "zgh_MA" },
|
|
{ name => "za" },
|
|
{ name => "za-CN" },
|
|
{ name => "zh", lcid => 0x00007804, ireadinglayout => 2, oemcp => 936, slist => ",", sscripts => "Hani Hans", sabbrevlangname => "CHS", sopentypelang => "ZHS", nativedigits => "0123456789" },
|
|
{ name => "zh-CN", lcid => 0x00000804, file => "zh_Hans_CN", sparent => "zh-Hans" },
|
|
{ name => "zh-CN_phoneb", lcid => 0x00050804, alias => "zh-CN" },
|
|
{ name => "zh-CN_stroke", lcid => 0x00020804, alias => "zh-CN" },
|
|
{ name => "zh-Hans", lcid => 0x00000004, group => 10 },
|
|
{ name => "zh-Hans-CN", alias => "zh-CN" },
|
|
{ name => "zh-Hans-CN-u-co-phonebk", alias => "zh-CN_phoneb" },
|
|
{ name => "zh-Hans-CN-u-co-stroke", alias => "zh-CN_stroke" },
|
|
{ name => "zh-Hans-HK", slist => ";", nativedigits => "" },
|
|
{ name => "zh-Hans-MO", slist => ";", nativedigits => "" },
|
|
{ name => "zh-Hans-SG", alias => "zh-SG" },
|
|
{ name => "zh-Hans-SG-u-co-phonebk", alias => "zh-SG_phoneb" },
|
|
{ name => "zh-Hans-SG-u-co-stroke", alias => "zh-SG_stroke" },
|
|
{ name => "zh-Hant", lcid => 0x00007c04, sortlocale => "zh-HK", ireadinglayout => 2, oemcp => 950, slist => ",", sscripts => "Hani Hant", group => 9, sabbrevlangname => "CHT", sopentypelang => "ZHH" },
|
|
{ name => "zh-Hant-HK", alias => "zh-HK" },
|
|
{ name => "zh-Hant-HK-u-co-unihan", alias => "zh-HK_radstr" },
|
|
{ name => "zh-Hant-MO", alias => "zh-MO" },
|
|
{ name => "zh-Hant-MO-u-co-stroke", alias => "zh-MO_stroke" },
|
|
{ name => "zh-Hant-MO-u-co-unihan", alias => "zh-MO_radstr" },
|
|
{ name => "zh-Hant-TW", alias => "zh-TW" },
|
|
{ name => "zh-Hant-TW-u-co-phonetic", alias => "zh-TW_pronun" },
|
|
{ name => "zh-Hant-TW-u-co-unihan", alias => "zh-TW_radstr" },
|
|
{ name => "zh-HK", lcid => 0x00000c04, file => "zh_Hant_HK", sparent => "zh-Hant", sabbrevlangname => "ZHH" },
|
|
{ name => "zh-HK_radstr", lcid => 0x00040c04, alias => "zh-HK" },
|
|
{ name => "zh-MO", lcid => 0x00001404, file => "zh_Hant_MO", sparent => "zh-Hant", sabbrevlangname => "ZHM", sopentypelang => "ZHT" },
|
|
{ name => "zh-MO_radstr", lcid => 0x00041404, alias => "zh-MO" },
|
|
{ name => "zh-MO_stroke", lcid => 0x00021404, alias => "zh-MO" },
|
|
{ name => "zh-SG", lcid => 0x00001004, file => "zh_Hans_SG", sparent => "zh-Hans", sabbrevlangname => "ZHI" },
|
|
{ name => "zh-SG_phoneb", lcid => 0x00051004, alias => "zh-SG" },
|
|
{ name => "zh-SG_stroke", lcid => 0x00021004, alias => "zh-SG" },
|
|
{ name => "zh-TW", lcid => 0x00000404, file => "zh_Hant_TW", sparent => "zh-Hant", sopentypelang => "ZHT" },
|
|
{ name => "zh-TW_pronun", lcid => 0x00030404, alias => "zh-TW" },
|
|
{ name => "zh-TW_radstr", lcid => 0x00040404, alias => "zh-TW" },
|
|
{ name => "zu", lcid => 0x00000035, oemcp => 850 },
|
|
{ name => "zu-ZA", lcid => 0x00000435 },
|
|
);
|
|
|
|
my @calendars =
|
|
(
|
|
{ id => 1, name => "Gregorian", itwodigityearmax => 2049 },
|
|
{ id => 2, type => "gregorian", locale => "en-US", itwodigityearmax => 2049 },
|
|
{ id => 3, type => "japanese", locale => "ja-JP", eras => [ 232..236 ] },
|
|
{ id => 4, type => "roc", locale => "zh-TW", eras => [ 1 ] },
|
|
{ id => 5, type => "dangi", locale => "ko-KR", eras => [ 0 ] },
|
|
{ id => 6, type => "islamic", locale => "ar-SA", itwodigityearmax => 1451 },
|
|
{ id => 7, type => "buddhist", locale => "th-TH", eras => [ 0 ] },
|
|
{ id => 8, type => "hebrew", locale => "he-IL", itwodigityearmax => 5810 },
|
|
{ id => 9, type => "gregorian", locale => "fr-FR", itwodigityearmax => 2049 },
|
|
{ id => 10, type => "gregorian", locale => "ar-SA", itwodigityearmax => 2049 },
|
|
{ id => 11, type => "gregorian", locale => "ar-SA", itwodigityearmax => 2049 },
|
|
{ id => 12, type => "gregorian", locale => "ar-SA", itwodigityearmax => 2049 },
|
|
{ id => 13, name => "Julian", locale => "en-US", itwodigityearmax => 2049 },
|
|
{ id => 14, name => "Japanese Lunisolar" },
|
|
{ id => 15, name => "Chinese Lunisolar" },
|
|
{ id => 16, name => "Saka" },
|
|
{ id => 17, name => "Lunar ETO Chinese" },
|
|
{ id => 18, name => "Lunar ETO Korean" },
|
|
{ id => 19, name => "Lunar ETO Rokuyou" },
|
|
{ id => 20, name => "Korean Lunisolar" },
|
|
{ id => 21, name => "Taiwan Lunisolar" },
|
|
{ id => 22, type => "persian", locale => "prs-AF", itwodigityearmax => 1429 },
|
|
{ id => 23, type => "islamic-umalqura", locale => "ar-SA", itwodigityearmax => 1451 },
|
|
);
|
|
|
|
my @geoids =
|
|
(
|
|
{ id => 2, name => "AG" }, # Antigua and Barbuda
|
|
{ id => 3, name => "AF" }, # Afghanistan
|
|
{ id => 4, name => "DZ" }, # Algeria
|
|
{ id => 5, name => "AZ" }, # Azerbaijan
|
|
{ id => 6, name => "AL" }, # Albania
|
|
{ id => 7, name => "AM" }, # Armenia
|
|
{ id => 8, name => "AD" }, # Andorra
|
|
{ id => 9, name => "AO" }, # Angola
|
|
{ id => 10, name => "AS" }, # American Samoa
|
|
{ id => 11, name => "AR" }, # Argentina
|
|
{ id => 12, name => "AU" }, # Australia
|
|
{ id => 14, name => "AT" }, # Austria
|
|
{ id => 17, name => "BH" }, # Bahrain
|
|
{ id => 18, name => "BB" }, # Barbados
|
|
{ id => 19, name => "BW" }, # Botswana
|
|
{ id => 20, name => "BM" }, # Bermuda
|
|
{ id => 21, name => "BE" }, # Belgium
|
|
{ id => 22, name => "BS" }, # Bahamas, The
|
|
{ id => 23, name => "BD" }, # Bangladesh
|
|
{ id => 24, name => "BZ" }, # Belize
|
|
{ id => 25, name => "BA" }, # Bosnia and Herzegovina
|
|
{ id => 26, name => "BO" }, # Bolivia
|
|
{ id => 27, name => "MM" }, # Myanmar
|
|
{ id => 28, name => "BJ" }, # Benin
|
|
{ id => 29, name => "BY" }, # Belarus
|
|
{ id => 30, name => "SB" }, # Solomon Islands
|
|
{ id => 32, name => "BR" }, # Brazil
|
|
{ id => 34, name => "BT" }, # Bhutan
|
|
{ id => 35, name => "BG" }, # Bulgaria
|
|
{ id => 37, name => "BN" }, # Brunei
|
|
{ id => 38, name => "BI" }, # Burundi
|
|
{ id => 39, name => "CA" }, # Canada
|
|
{ id => 40, name => "KH" }, # Cambodia
|
|
{ id => 41, name => "TD" }, # Chad
|
|
{ id => 42, name => "LK" }, # Sri Lanka
|
|
{ id => 43, name => "CG" }, # Congo
|
|
{ id => 44, name => "CD" }, # Congo (DRC)
|
|
{ id => 45, name => "CN" }, # China
|
|
{ id => 46, name => "CL" }, # Chile
|
|
{ id => 49, name => "CM" }, # Cameroon
|
|
{ id => 50, name => "KM" }, # Comoros
|
|
{ id => 51, name => "CO" }, # Colombia
|
|
{ id => 54, name => "CR" }, # Costa Rica
|
|
{ id => 55, name => "CF" }, # Central African Republic
|
|
{ id => 56, name => "CU" }, # Cuba
|
|
{ id => 57, name => "CV" }, # Cape Verde
|
|
{ id => 59, name => "CY" }, # Cyprus
|
|
{ id => 61, name => "DK" }, # Denmark
|
|
{ id => 62, name => "DJ" }, # Djibouti
|
|
{ id => 63, name => "DM" }, # Dominica
|
|
{ id => 65, name => "DO" }, # Dominican Republic
|
|
{ id => 66, name => "EC" }, # Ecuador
|
|
{ id => 67, name => "EG" }, # Egypt
|
|
{ id => 68, name => "IE" }, # Ireland
|
|
{ id => 69, name => "GQ" }, # Equatorial Guinea
|
|
{ id => 70, name => "EE" }, # Estonia
|
|
{ id => 71, name => "ER" }, # Eritrea
|
|
{ id => 72, name => "SV" }, # El Salvador
|
|
{ id => 73, name => "ET" }, # Ethiopia
|
|
{ id => 75, name => "CZ" }, # Czech Republic
|
|
{ id => 77, name => "FI" }, # Finland
|
|
{ id => 78, name => "FJ" }, # Fiji Islands
|
|
{ id => 80, name => "FM" }, # Micronesia
|
|
{ id => 81, name => "FO" }, # Faroe Islands
|
|
{ id => 84, name => "FR" }, # France
|
|
{ id => 86, name => "GM" }, # Gambia, The
|
|
{ id => 87, name => "GA" }, # Gabon
|
|
{ id => 88, name => "GE" }, # Georgia
|
|
{ id => 89, name => "GH" }, # Ghana
|
|
{ id => 90, name => "GI" }, # Gibraltar
|
|
{ id => 91, name => "GD" }, # Grenada
|
|
{ id => 93, name => "GL" }, # Greenland
|
|
{ id => 94, name => "DE" }, # Germany
|
|
{ id => 98, name => "GR" }, # Greece
|
|
{ id => 99, name => "GT" }, # Guatemala
|
|
{ id => 100, name => "GN" }, # Guinea
|
|
{ id => 101, name => "GY" }, # Guyana
|
|
{ id => 103, name => "HT" }, # Haiti
|
|
{ id => 104, name => "HK" }, # Hong Kong S.A.R.
|
|
{ id => 106, name => "HN" }, # Honduras
|
|
{ id => 108, name => "HR" }, # Croatia
|
|
{ id => 109, name => "HU" }, # Hungary
|
|
{ id => 110, name => "IS" }, # Iceland
|
|
{ id => 111, name => "ID" }, # Indonesia
|
|
{ id => 113, name => "IN" }, # India
|
|
{ id => 114, name => "IO" }, # British Indian Ocean Territory
|
|
{ id => 116, name => "IR" }, # Iran
|
|
{ id => 117, name => "IL" }, # Israel
|
|
{ id => 118, name => "IT" }, # Italy
|
|
{ id => 119, name => "CI" }, # Côte d'Ivoire
|
|
{ id => 121, name => "IQ" }, # Iraq
|
|
{ id => 122, name => "JP" }, # Japan
|
|
{ id => 124, name => "JM" }, # Jamaica
|
|
{ id => 125, name => "SJ" }, # Jan Mayen
|
|
{ id => 126, name => "JO" }, # Jordan
|
|
{ id => 127, parent => "UM" }, # Johnston Atoll
|
|
{ id => 129, name => "KE" }, # Kenya
|
|
{ id => 130, name => "KG" }, # Kyrgyzstan
|
|
{ id => 131, name => "KP" }, # North Korea
|
|
{ id => 133, name => "KI" }, # Kiribati
|
|
{ id => 134, name => "KR" }, # Korea
|
|
{ id => 136, name => "KW" }, # Kuwait
|
|
{ id => 137, name => "KZ" }, # Kazakhstan
|
|
{ id => 138, name => "LA" }, # Laos
|
|
{ id => 139, name => "LB" }, # Lebanon
|
|
{ id => 140, name => "LV" }, # Latvia
|
|
{ id => 141, name => "LT" }, # Lithuania
|
|
{ id => 142, name => "LR" }, # Liberia
|
|
{ id => 143, name => "SK" }, # Slovakia
|
|
{ id => 145, name => "LI" }, # Liechtenstein
|
|
{ id => 146, name => "LS" }, # Lesotho
|
|
{ id => 147, name => "LU" }, # Luxembourg
|
|
{ id => 148, name => "LY" }, # Libya
|
|
{ id => 149, name => "MG" }, # Madagascar
|
|
{ id => 151, name => "MO" }, # Macao S.A.R.
|
|
{ id => 152, name => "MD" }, # Moldova
|
|
{ id => 154, name => "MN" }, # Mongolia
|
|
{ id => 156, name => "MW" }, # Malawi
|
|
{ id => 157, name => "ML" }, # Mali
|
|
{ id => 158, name => "MC" }, # Monaco
|
|
{ id => 159, name => "MA" }, # Morocco
|
|
{ id => 160, name => "MU" }, # Mauritius
|
|
{ id => 162, name => "MR" }, # Mauritania
|
|
{ id => 163, name => "MT" }, # Malta
|
|
{ id => 164, name => "OM" }, # Oman
|
|
{ id => 165, name => "MV" }, # Maldives
|
|
{ id => 166, name => "MX" }, # Mexico
|
|
{ id => 167, name => "MY" }, # Malaysia
|
|
{ id => 168, name => "MZ" }, # Mozambique
|
|
{ id => 173, name => "NE" }, # Niger
|
|
{ id => 174, name => "VU" }, # Vanuatu
|
|
{ id => 175, name => "NG" }, # Nigeria
|
|
{ id => 176, name => "NL" }, # Netherlands
|
|
{ id => 177, name => "NO" }, # Norway
|
|
{ id => 178, name => "NP" }, # Nepal
|
|
{ id => 180, name => "NR" }, # Nauru
|
|
{ id => 181, name => "SR" }, # Suriname
|
|
{ id => 182, name => "NI" }, # Nicaragua
|
|
{ id => 183, name => "NZ" }, # New Zealand
|
|
{ id => 184, name => "PS" }, # Palestinian Authority
|
|
{ id => 185, name => "PY" }, # Paraguay
|
|
{ id => 187, name => "PE" }, # Peru
|
|
{ id => 190, name => "PK" }, # Pakistan
|
|
{ id => 191, name => "PL" }, # Poland
|
|
{ id => 192, name => "PA" }, # Panama
|
|
{ id => 193, name => "PT" }, # Portugal
|
|
{ id => 194, name => "PG" }, # Papua New Guinea
|
|
{ id => 195, name => "PW" }, # Palau
|
|
{ id => 196, name => "GW" }, # Guinea-Bissau
|
|
{ id => 197, name => "QA" }, # Qatar
|
|
{ id => 198, name => "RE" }, # Reunion
|
|
{ id => 199, name => "MH" }, # Marshall Islands
|
|
{ id => 200, name => "RO" }, # Romania
|
|
{ id => 201, name => "PH" }, # Philippines
|
|
{ id => 202, name => "PR" }, # Puerto Rico
|
|
{ id => 203, name => "RU" }, # Russia
|
|
{ id => 204, name => "RW" }, # Rwanda
|
|
{ id => 205, name => "SA" }, # Saudi Arabia
|
|
{ id => 206, name => "PM" }, # St. Pierre and Miquelon
|
|
{ id => 207, name => "KN" }, # St. Kitts and Nevis
|
|
{ id => 208, name => "SC" }, # Seychelles
|
|
{ id => 209, name => "ZA" }, # South Africa
|
|
{ id => 210, name => "SN" }, # Senegal
|
|
{ id => 212, name => "SI" }, # Slovenia
|
|
{ id => 213, name => "SL" }, # Sierra Leone
|
|
{ id => 214, name => "SM" }, # San Marino
|
|
{ id => 215, name => "SG" }, # Singapore
|
|
{ id => 216, name => "SO" }, # Somalia
|
|
{ id => 217, name => "ES" }, # Spain
|
|
{ id => 218, name => "LC" }, # St. Lucia
|
|
{ id => 219, name => "SD" }, # Sudan
|
|
{ id => 220, name => "SJ" }, # Svalbard
|
|
{ id => 221, name => "SE" }, # Sweden
|
|
{ id => 222, name => "SY" }, # Syria
|
|
{ id => 223, name => "CH" }, # Switzerland
|
|
{ id => 224, name => "AE" }, # United Arab Emirates
|
|
{ id => 225, name => "TT" }, # Trinidad and Tobago
|
|
{ id => 227, name => "TH" }, # Thailand
|
|
{ id => 228, name => "TJ" }, # Tajikistan
|
|
{ id => 231, name => "TO" }, # Tonga
|
|
{ id => 232, name => "TG" }, # Togo
|
|
{ id => 233, name => "ST" }, # São Tomé and Príncipe
|
|
{ id => 234, name => "TN" }, # Tunisia
|
|
{ id => 235, name => "TR" }, # Turkey
|
|
{ id => 236, name => "TV" }, # Tuvalu
|
|
{ id => 237, name => "TW" }, # Taiwan
|
|
{ id => 238, name => "TM" }, # Turkmenistan
|
|
{ id => 239, name => "TZ" }, # Tanzania
|
|
{ id => 240, name => "UG" }, # Uganda
|
|
{ id => 241, name => "UA" }, # Ukraine
|
|
{ id => 242, name => "GB" }, # United Kingdom
|
|
{ id => 244, name => "US" }, # United States
|
|
{ id => 245, name => "BF" }, # Burkina Faso
|
|
{ id => 246, name => "UY" }, # Uruguay
|
|
{ id => 247, name => "UZ" }, # Uzbekistan
|
|
{ id => 248, name => "VC" }, # St. Vincent and the Grenadines
|
|
{ id => 249, name => "VE" }, # Bolivarian Republic of Venezuela
|
|
{ id => 251, name => "VN" }, # Vietnam
|
|
{ id => 252, name => "VI" }, # Virgin Islands
|
|
{ id => 253, name => "VA" }, # Vatican City
|
|
{ id => 254, name => "NA" }, # Namibia
|
|
{ id => 257, name => "EH" }, # Western Sahara (disputed)
|
|
{ id => 258, parent => "UM" }, # Wake Island
|
|
{ id => 259, name => "WS" }, # Samoa
|
|
{ id => 260, name => "SZ" }, # Swaziland
|
|
{ id => 261, name => "YE" }, # Yemen
|
|
{ id => 263, name => "ZM" }, # Zambia
|
|
{ id => 264, name => "ZW" }, # Zimbabwe
|
|
{ id => 269, name => "CS" }, # Serbia and Montenegro (Former)
|
|
{ id => 270, name => "ME" }, # Montenegro
|
|
{ id => 271, name => "RS" }, # Serbia
|
|
{ id => 273, name => "CW" }, # Curaçao
|
|
{ id => 276, name => "SS" }, # South Sudan
|
|
{ id => 300, name => "AI" }, # Anguilla
|
|
{ id => 301, name => "AQ" }, # Antarctica
|
|
{ id => 302, name => "AW" }, # Aruba
|
|
{ id => 303, parent => "SH" }, # Ascension Island
|
|
{ id => 304, parent => "053" }, # Ashmore and Cartier Islands
|
|
{ id => 305, parent => "UM" }, # Baker Island
|
|
{ id => 306, name => "BV" }, # Bouvet Island
|
|
{ id => 307, name => "KY" }, # Cayman Islands
|
|
{ id => 308, name => "830", parent => "155" }, # Channel Islands
|
|
{ id => 309, name => "CX" }, # Christmas Island
|
|
{ id => 310, parent => "009" }, # Clipperton Island
|
|
{ id => 311, name => "CC" }, # Cocos (Keeling) Islands
|
|
{ id => 312, name => "CK" }, # Cook Islands
|
|
{ id => 313, parent => "053" }, # Coral Sea Islands
|
|
{ id => 314, parent => "IO" }, # Diego Garcia
|
|
{ id => 315, name => "FK" }, # Falkland Islands (Islas Malvinas)
|
|
{ id => 317, name => "GF" }, # French Guiana
|
|
{ id => 318, name => "PF" }, # French Polynesia
|
|
{ id => 319, name => "TF" }, # French Southern and Antarctic Lands
|
|
{ id => 321, name => "GP" }, # Guadeloupe
|
|
{ id => 322, name => "GU" }, # Guam
|
|
{ id => 323 }, # Guantanamo Bay
|
|
{ id => 324, name => "GG" }, # Guernsey
|
|
{ id => 325, name => "HM" }, # Heard Island and McDonald Islands
|
|
{ id => 326, parent => "UM" }, # Howland Island
|
|
{ id => 327, parent => "UM" }, # Jarvis Island
|
|
{ id => 328, name => "JE" }, # Jersey
|
|
{ id => 329, parent => "UM" }, # Kingman Reef
|
|
{ id => 330, name => "MQ" }, # Martinique
|
|
{ id => 331, name => "YT" }, # Mayotte
|
|
{ id => 332, name => "MS" }, # Montserrat
|
|
{ id => 333, name => "AN", region => 1 }, # Netherlands Antilles (Former)
|
|
{ id => 334, name => "NC" }, # New Caledonia
|
|
{ id => 335, name => "NU" }, # Niue
|
|
{ id => 336, name => "NF" }, # Norfolk Island
|
|
{ id => 337, name => "MP" }, # Northern Mariana Islands
|
|
{ id => 338, parent => "UM" }, # Palmyra Atoll
|
|
{ id => 339, name => "PN" }, # Pitcairn Islands
|
|
{ id => 340, parent => "MP" }, # Rota Island
|
|
{ id => 341, parent => "MP" }, # Saipan
|
|
{ id => 342, name => "GS" }, # South Georgia and the South Sandwich Islands
|
|
{ id => 343, name => "SH" }, # St. Helena
|
|
{ id => 346, parent => "MP" }, # Tinian Island
|
|
{ id => 347, name => "TK" }, # Tokelau
|
|
{ id => 348, parent => "SH" }, # Tristan da Cunha
|
|
{ id => 349, name => "TC" }, # Turks and Caicos Islands
|
|
{ id => 351, name => "VG" }, # Virgin Islands, British
|
|
{ id => 352, name => "WF" }, # Wallis and Futuna
|
|
{ id => 742, name => "002" }, # Africa
|
|
{ id => 2129, name => "142" }, # Asia
|
|
{ id => 10541, name => "150" }, # Europe
|
|
{ id => 15126, name => "IM" }, # Man, Isle of
|
|
{ id => 19618, name => "MK" }, # Macedonia, Former Yugoslav Republic of
|
|
{ id => 20900, name => "054" }, # Melanesia
|
|
{ id => 21206, name => "057" }, # Micronesia
|
|
{ id => 21242, parent => "UM" }, # Midway Islands
|
|
{ id => 23581, name => "021" }, # Northern America
|
|
{ id => 26286, name => "061" }, # Polynesia
|
|
{ id => 27082, name => "013" }, # Central America
|
|
{ id => 27114, name => "009" }, # Oceania
|
|
{ id => 30967, name => "SX" }, # Sint Maarten (Dutch part)
|
|
{ id => 31396, name => "005" }, # South America
|
|
{ id => 31706, name => "MF" }, # Saint Martin (French part)
|
|
{ id => 39070, name => "001" }, # World
|
|
{ id => 42483, name => "011" }, # Western Africa
|
|
{ id => 42484, name => "017" }, # Middle Africa
|
|
{ id => 42487, name => "015" }, # Northern Africa
|
|
{ id => 47590, name => "143" }, # Central Asia
|
|
{ id => 47599, name => "035" }, # South-Eastern Asia
|
|
{ id => 47600, name => "030" }, # Eastern Asia
|
|
{ id => 47603, name => "014" }, # Eastern Africa
|
|
{ id => 47609, name => "151" }, # Eastern Europe
|
|
{ id => 47610, name => "039" }, # Southern Europe
|
|
{ id => 47611, name => "145" }, # Middle East
|
|
{ id => 47614, name => "034" }, # Southern Asia
|
|
{ id => 7299303, name => "TL" }, # Democratic Republic of Timor-Leste
|
|
{ id => 9914689, name => "XK" }, # Kosovo
|
|
{ id => 10026358, name => "019" }, # Americas
|
|
{ id => 10028789, name => "AX" }, # Åland Islands
|
|
{ id => 10039880, name => "029", sintlsymbol => "XCD" }, # Caribbean
|
|
{ id => 10039882, name => "154" }, # Northern Europe
|
|
{ id => 10039883, name => "018" }, # Southern Africa
|
|
{ id => 10210824, name => "155" }, # Western Europe
|
|
{ id => 10210825, name => "053" }, # Australia and New Zealand
|
|
{ id => 161832015, name => "BL" }, # Saint Barthélemy
|
|
{ id => 161832256, name => "UM" }, # U.S. Minor Outlying Islands
|
|
{ id => 161832257, name => "419", parent => "019" }, # Latin America and the Caribbean
|
|
{ id => 161832258, name => "BQ" }, # Bonaire, Sint Eustatius and Saba
|
|
);
|
|
|
|
my @cp2uni = ();
|
|
my @glyph2uni = ();
|
|
my @lead_bytes = ();
|
|
my @uni2cp = ();
|
|
my @tolower_table = ();
|
|
my @toupper_table = ();
|
|
my @digitmap_table = ();
|
|
my @halfwidth_table = ();
|
|
my @fullwidth_table = ();
|
|
my @cjk_compat_table = ();
|
|
my @chinese_traditional_table = ();
|
|
my @chinese_simplified_table = ();
|
|
my @category_table = ();
|
|
my @initial_joining_table = ();
|
|
my @direction_table = ();
|
|
my @decomp_table = ();
|
|
my @combining_class_table = ();
|
|
my @decomp_compat_table = ();
|
|
my @comp_exclusions = ();
|
|
my @idna_decomp_table = ();
|
|
my @idna_disallowed = ();
|
|
my %registry_keys;
|
|
my $default_char;
|
|
my $default_wchar;
|
|
|
|
my %joining_forms =
|
|
(
|
|
"isolated" => [],
|
|
"final" => [],
|
|
"initial" => [],
|
|
"medial" => []
|
|
);
|
|
|
|
my $current_data_file;
|
|
|
|
sub to_utf16(@)
|
|
{
|
|
my @ret;
|
|
foreach my $ch (@_)
|
|
{
|
|
if ($ch < 0x10000)
|
|
{
|
|
push @ret, $ch;
|
|
}
|
|
else
|
|
{
|
|
my $val = $ch - 0x10000;
|
|
push @ret, 0xd800 | ($val >> 10), 0xdc00 | ($val & 0x3ff);
|
|
}
|
|
}
|
|
return @ret;
|
|
}
|
|
|
|
################################################################
|
|
# fetch a unicode.org file and open it
|
|
sub open_data_file($@)
|
|
{
|
|
my ($id, $name) = @_;
|
|
my $data = $data_files{$id};
|
|
my $cache = ($ENV{XDG_CACHE_HOME} || "$ENV{HOME}/.cache") . "/wine";
|
|
local *FILE;
|
|
|
|
my $url = $data->{url};
|
|
my $filename = "$cache/" . ($data->{name} || ($url =~ s/.*\/([^\/]+)$/$1/r));
|
|
unless (-f $filename)
|
|
{
|
|
print "Fetching $url...\n";
|
|
system "mkdir", "-p", $cache;
|
|
!system "wget", "-q", "-O", $filename, $url or die "cannot fetch $url";
|
|
}
|
|
|
|
my $sha = Digest::SHA->new( "sha256" )->addfile( $filename )->hexdigest;
|
|
die "invalid checksum $sha for $filename" unless $sha eq $data->{sha};
|
|
|
|
if ($filename =~ /\.zip$/)
|
|
{
|
|
open FILE, "-|", "unzip", "-p", $filename, $name or die "cannot extract $name from $filename";
|
|
}
|
|
elsif ($filename =~ /\.tar\.gz$/)
|
|
{
|
|
open FILE, "-|", "tar", "-x", "-f", $filename, "-O", $name or die "cannot extract $name from $filename";
|
|
}
|
|
else
|
|
{
|
|
open FILE, "<$filename" or die "cannot open $filename";
|
|
}
|
|
$current_data_file = $name ? "$url:$name" : $url;
|
|
return *FILE;
|
|
}
|
|
|
|
################################################################
|
|
# load a unicode.org file as XML data
|
|
sub load_xml_data_file($@)
|
|
{
|
|
my ($id, $name) = @_;
|
|
my $FILE = open_data_file( $id, $name );
|
|
my $xml = XML::LibXML->load_xml( IO => $FILE );
|
|
close FILE;
|
|
return $xml;
|
|
}
|
|
|
|
################################################################
|
|
# recursively get the decomposition for a character
|
|
sub get_decomposition($$);
|
|
sub get_decomposition($$)
|
|
{
|
|
my ($char, $table) = @_;
|
|
my @ret;
|
|
|
|
return $char unless defined $table->[$char];
|
|
foreach my $ch (@{$table->[$char]})
|
|
{
|
|
push @ret, get_decomposition( $ch, $table );
|
|
}
|
|
return @ret;
|
|
}
|
|
|
|
################################################################
|
|
# get the composition that results in a given character
|
|
sub get_composition($$)
|
|
{
|
|
my ($ch, $compat) = @_;
|
|
return () unless defined $decomp_table[$ch]; # no decomposition
|
|
my @ret = @{$decomp_table[$ch]};
|
|
return () if @ret < 2; # singleton decomposition
|
|
return () if $comp_exclusions[$ch]; # composition exclusion
|
|
return () if $combining_class_table[$ch]; # non-starter
|
|
return () if $combining_class_table[$ret[0]]; # first char is non-starter
|
|
return () if $compat == 1 && !defined $decomp_table[$ret[0]] &&
|
|
defined $decomp_compat_table[$ret[0]]; # first char has compat decomposition
|
|
return () if $compat == 2 && !defined $decomp_table[$ret[0]] &&
|
|
defined $idna_decomp_table[$ret[0]]; # first char has IDNA decomposition
|
|
return () if $compat == 2 && defined $idna_decomp_table[$ret[0]] &&
|
|
defined $idna_decomp_table[$idna_decomp_table[$ret[0]]->[0]]; # first char's decomposition has IDNA decomposition
|
|
return () if $compat == 2 && defined $idna_decomp_table[$ret[1]]; # second char has IDNA decomposition
|
|
return @ret;
|
|
}
|
|
|
|
################################################################
|
|
# recursively build decompositions
|
|
sub build_decompositions(@)
|
|
{
|
|
my @src = @_;
|
|
my @dst;
|
|
|
|
for (my $i = 0; $i < @src; $i++)
|
|
{
|
|
next unless defined $src[$i];
|
|
my @decomp = to_utf16( get_decomposition( $i, \@src ));
|
|
$dst[$i] = \@decomp;
|
|
}
|
|
return @dst;
|
|
}
|
|
|
|
################################################################
|
|
# compose Hangul sequences
|
|
sub compose_hangul(@)
|
|
{
|
|
my $SBASE = 0xac00;
|
|
my $LBASE = 0x1100;
|
|
my $VBASE = 0x1161;
|
|
my $TBASE = 0x11a7;
|
|
my $LCOUNT = 19;
|
|
my $VCOUNT = 21;
|
|
my $TCOUNT = 28;
|
|
my $NCOUNT = $VCOUNT * $TCOUNT;
|
|
my $SCOUNT = $LCOUNT * $NCOUNT;
|
|
|
|
my @seq = @_;
|
|
my @ret;
|
|
my $i;
|
|
|
|
for ($i = 0; $i < @seq; $i++)
|
|
{
|
|
my $ch = $seq[$i];
|
|
if ($ch >= $LBASE && $ch < $LBASE + $LCOUNT && $i < @seq - 1 &&
|
|
$seq[$i+1] >= $VBASE && $seq[$i+1] < $VBASE + $VCOUNT)
|
|
{
|
|
$ch = $SBASE + (($seq[$i] - $LBASE) * $VCOUNT + ($seq[$i+1] - $VBASE)) * $TCOUNT;
|
|
$i++;
|
|
}
|
|
if ($ch >= $SBASE && $ch < $SBASE + $SCOUNT && !(($ch - $SBASE) % $TCOUNT) && $i < @seq - 1 &&
|
|
$seq[$i+1] > $TBASE && $seq[$i+1] < $TBASE + $TCOUNT)
|
|
{
|
|
$ch += $seq[$i+1] - $TBASE;
|
|
$i++;
|
|
}
|
|
push @ret, $ch;
|
|
}
|
|
return @ret;
|
|
}
|
|
|
|
################################################################
|
|
# remove linguistic-only mappings from the case table
|
|
sub remove_linguistic_mappings($$)
|
|
{
|
|
my ($upper, $lower) = @_;
|
|
|
|
# remove case mappings that don't round-trip
|
|
|
|
for (my $i = 0; $i < @{$upper}; $i++)
|
|
{
|
|
next unless defined ${$upper}[$i];
|
|
my $ch = ${$upper}[$i];
|
|
${$upper}[$i] = undef unless defined ${$lower}[$ch] && ${$lower}[$ch] == $i;
|
|
}
|
|
for (my $i = 0; $i < @{$lower}; $i++)
|
|
{
|
|
next unless defined ${$lower}[$i];
|
|
my $ch = ${$lower}[$i];
|
|
${$lower}[$i] = undef unless defined ${$upper}[$ch] && ${$upper}[$ch] == $i;
|
|
}
|
|
}
|
|
|
|
################################################################
|
|
# read in the Unicode database files
|
|
sub load_data()
|
|
{
|
|
my $start;
|
|
|
|
# now build mappings from the decomposition field of the Unicode database
|
|
|
|
my $UNICODE_DATA = open_data_file( "ucd", "UnicodeData.txt" );
|
|
while (<$UNICODE_DATA>)
|
|
{
|
|
# Decode the fields ...
|
|
my ($code, $name, $cat, $comb, $bidi,
|
|
$decomp, $dec, $dig, $num, $mirror,
|
|
$oldname, $comment, $upper, $lower, $title) = split /;/;
|
|
my $src = hex $code;
|
|
|
|
die "unknown category $cat" unless defined $categories{$cat};
|
|
die "unknown directionality $bidi" unless defined $directions{$bidi};
|
|
|
|
$category_table[$src] = $categories{$cat};
|
|
$direction_table[$src] = $bidi;
|
|
if ($cat eq "Mn" || $cat eq "Me" || $cat eq "Cf")
|
|
{
|
|
$initial_joining_table[$src] = $joining_types{"T"};
|
|
}
|
|
else
|
|
{
|
|
$initial_joining_table[$src] = $joining_types{"U"};
|
|
}
|
|
|
|
if ($lower ne "")
|
|
{
|
|
$tolower_table[$src] = hex $lower;
|
|
}
|
|
if ($upper ne "")
|
|
{
|
|
$toupper_table[$src] = hex $upper;
|
|
}
|
|
if ($dec ne "")
|
|
{
|
|
$category_table[$src] |= $ctype{"digit"};
|
|
}
|
|
if ($dig ne "")
|
|
{
|
|
$digitmap_table[$src] = ord $dig;
|
|
}
|
|
$combining_class_table[$src] = ($cat ne "Co") ? $comb : 0x100; # Private Use
|
|
|
|
$category_table[$src] |= $ctype{"nonspacing"} if $bidi eq "NSM";
|
|
$category_table[$src] |= $ctype{"diacritic"} if $name =~ /^(COMBINING)|(MODIFIER LETTER)\W/;
|
|
$category_table[$src] |= $ctype{"vowelmark"} if $name =~ /\sVOWEL/ || $oldname =~ /\sVOWEL/;
|
|
$category_table[$src] |= $ctype{"halfwidth"} if $name =~ /^HALFWIDTH\s/;
|
|
$category_table[$src] |= $ctype{"fullwidth"} if $name =~ /^FULLWIDTH\s/;
|
|
$category_table[$src] |= $ctype{"hiragana"} if $name =~ /(HIRAGANA)|(\WKANA\W)/;
|
|
$category_table[$src] |= $ctype{"katakana"} if $name =~ /(KATAKANA)|(\WKANA\W)/;
|
|
$category_table[$src] |= $ctype{"ideograph"} if $name =~ /^<CJK Ideograph/;
|
|
$category_table[$src] |= $ctype{"ideograph"} if $name =~ /^CJK COMPATIBILITY IDEOGRAPH/;
|
|
$category_table[$src] |= $ctype{"ideograph"} if $name =~ /^HANGZHOU/;
|
|
$category_table[$src] |= $ctype{"highsurrogate"} if $name =~ /High Surrogate/;
|
|
$category_table[$src] |= $ctype{"lowsurrogate"} if $name =~ /Low Surrogate/;
|
|
|
|
# copy the category and direction for everything between First/Last pairs
|
|
if ($name =~ /, First>/) { $start = $src; }
|
|
if ($name =~ /, Last>/)
|
|
{
|
|
while ($start < $src)
|
|
{
|
|
$category_table[$start] = $category_table[$src];
|
|
$direction_table[$start] = $direction_table[$src];
|
|
$combining_class_table[$start] = $combining_class_table[$src];
|
|
$start++;
|
|
}
|
|
}
|
|
|
|
next if $decomp eq ""; # no decomposition, skip it
|
|
|
|
if ($decomp =~ /^<([a-zA-Z]+)>\s+([0-9a-fA-F]+)/)
|
|
{
|
|
my @seq = map { hex $_; } (split /\s+/, (split /\s+/, $decomp, 2)[1]);
|
|
$decomp_compat_table[$src] = \@seq;
|
|
}
|
|
|
|
if ($decomp =~ /^<([a-zA-Z]+)>\s+([0-9a-fA-F]+)$/)
|
|
{
|
|
# decomposition of the form "<foo> 1234" -> use char if type is known
|
|
my $dst = hex $2;
|
|
if ($1 eq "narrow")
|
|
{
|
|
$halfwidth_table[$dst] = $src;
|
|
$fullwidth_table[$src] = $dst;
|
|
}
|
|
elsif ($1 eq "wide")
|
|
{
|
|
next if $dst == 0x5c; # don't remap backslash
|
|
$fullwidth_table[$dst] = $src;
|
|
$halfwidth_table[$src] = $dst;
|
|
}
|
|
elsif ($1 eq "font" || $1 eq "square" || $1 eq "circle")
|
|
{
|
|
$fullwidth_table[$src] = $dst if $src >= 0x10000;
|
|
}
|
|
elsif ($1 eq "isolated" || $1 eq "final" || $1 eq "initial" || $1 eq "medial")
|
|
{
|
|
${joining_forms{$1}}[$dst] = $src;
|
|
}
|
|
}
|
|
elsif ($decomp =~ /^<compat>\s+0020\s+([0-9a-fA-F]+)/)
|
|
{
|
|
# decomposition "<compat> 0020 1234" -> combining accent
|
|
}
|
|
elsif ($decomp =~ /^([0-9a-fA-F]+)/)
|
|
{
|
|
# store decomposition
|
|
if ($decomp =~ /^([0-9a-fA-F]+)\s+([0-9a-fA-F]+)$/)
|
|
{
|
|
$decomp_table[$src] = $decomp_compat_table[$src] = [ hex $1, hex $2 ];
|
|
}
|
|
elsif ($decomp =~ /^([0-9a-fA-F]+)$/)
|
|
{
|
|
my $dst = hex $1;
|
|
# Single char decomposition
|
|
$decomp_table[$src] = $decomp_compat_table[$src] = [ $dst ];
|
|
if ($name =~ /^CJK COMPATIBILITY IDEOGRAPH/)
|
|
{
|
|
$cjk_compat_table[$src] = $dst;
|
|
$fullwidth_table[$src] = $dst if $src >= 0x10000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
close $UNICODE_DATA;
|
|
|
|
# patch the category of some special characters
|
|
|
|
for (my $i = 0; $i < @decomp_table; $i++)
|
|
{
|
|
next unless defined $decomp_table[$i];
|
|
$category_table[$i] |= $category_table[$decomp_table[$i]->[0]];
|
|
}
|
|
foreach my $cat (keys %special_categories)
|
|
{
|
|
my $flag = $ctype{$cat};
|
|
foreach my $i (@{$special_categories{$cat}}) { $category_table[$i] |= $flag; }
|
|
}
|
|
for (my $i = 0; $i < @decomp_compat_table; $i++)
|
|
{
|
|
next unless defined $decomp_compat_table[$i];
|
|
next unless @{$decomp_compat_table[$i]} == 2;
|
|
$category_table[$i] |= $category_table[$decomp_compat_table[$i]->[1]] & $ctype{"diacritic"};
|
|
}
|
|
|
|
# load the composition exclusions
|
|
|
|
my $EXCL = open_data_file( "ucd", "CompositionExclusions.txt" );
|
|
while (<$EXCL>)
|
|
{
|
|
s/\#.*//; # remove comments
|
|
if (/^([0-9a-fA-F]+)\.\.([0-9a-fA-F]+)\s*$/)
|
|
{
|
|
foreach my $i (hex $1 .. hex $2) { $comp_exclusions[$i] = 1; }
|
|
}
|
|
elsif (/^([0-9a-fA-F]+)\s*$/)
|
|
{
|
|
$comp_exclusions[hex $1] = 1;
|
|
}
|
|
}
|
|
close $EXCL;
|
|
|
|
# load the IDNA mappings
|
|
|
|
@idna_decomp_table = @decomp_compat_table;
|
|
my $IDNA = open_data_file( "idna", "IdnaMappingTable.txt" );
|
|
while (<$IDNA>)
|
|
{
|
|
s/\#.*//; # remove comments
|
|
next if /^\s*$/;
|
|
my ($char, $type, $mapping) = split /;/;
|
|
my ($ch1, $ch2);
|
|
if ($char =~ /([0-9a-fA-F]+)\.\.([0-9a-fA-F]+)/)
|
|
{
|
|
$ch1 = hex $1;
|
|
$ch2 = hex $2;
|
|
}
|
|
elsif ($char =~ /([0-9a-fA-F]+)/)
|
|
{
|
|
$ch1 = $ch2 = hex $1;
|
|
}
|
|
|
|
if ($type =~ /mapped/ || $type =~ /deviation/)
|
|
{
|
|
$mapping =~ s/^\s*(([0-9a-fA-F]+\s+)+)\s*$/$1/;
|
|
my @seq = map { hex $_; } split /\s+/, $mapping;
|
|
foreach my $i ($ch1 .. $ch2) { $idna_decomp_table[$i] = @seq ? \@seq : [ 0 ]; }
|
|
}
|
|
elsif ($type =~ /valid/)
|
|
{
|
|
}
|
|
elsif ($type =~ /ignored/)
|
|
{
|
|
foreach my $i ($ch1 .. $ch2) { $idna_decomp_table[$i] = [ 0 ]; }
|
|
}
|
|
elsif ($type =~ /disallowed/)
|
|
{
|
|
foreach my $i ($ch1 .. $ch2)
|
|
{
|
|
$idna_decomp_table[$i] = undef;
|
|
$idna_disallowed[$i] = 1;
|
|
}
|
|
}
|
|
}
|
|
close $IDNA;
|
|
|
|
# load the Unihan mappings
|
|
|
|
my $UNIHAN = open_data_file( "unihan", "Unihan_Variants.txt" );
|
|
while (<$UNIHAN>)
|
|
{
|
|
s/\#.*//; # remove comments
|
|
next if /^\s*$/;
|
|
if (/^U\+([0-9a-fA-F]{4})\s+kTraditionalVariant\s+U\+([0-9a-fA-F]{4})$/)
|
|
{
|
|
next if hex $1 < 0x4dc0; # skip extension A
|
|
$chinese_traditional_table[hex $1] = hex $2;
|
|
}
|
|
elsif (/^U\+([0-9a-fA-F]{4})\s+kSimplifiedVariant\s+U\+([0-9a-fA-F]{4})$/)
|
|
{
|
|
next if hex $1 < 0x4dc0; # skip extension A
|
|
$chinese_simplified_table[hex $1] = hex $2;
|
|
}
|
|
}
|
|
close $UNIHAN;
|
|
foreach my $i (0xf900..0xfaff)
|
|
{
|
|
next unless defined $cjk_compat_table[$i];
|
|
next if defined $chinese_simplified_table[$cjk_compat_table[$i]];
|
|
$chinese_simplified_table[$i] = $cjk_compat_table[$i];
|
|
}
|
|
}
|
|
|
|
|
|
################################################################
|
|
# add a new registry key
|
|
sub add_registry_key($$$)
|
|
{
|
|
my ($base, $key, $defval) = @_;
|
|
$registry_keys{"$base\\$key"} = [ $defval ] unless defined $registry_keys{"$base\\$key"};
|
|
}
|
|
|
|
################################################################
|
|
# add a new registry value with explicit type
|
|
sub add_registry_value($$$$)
|
|
{
|
|
my ($base, $key, $name, $value) = @_;
|
|
add_registry_key( $base, $key, undef );
|
|
push @{$registry_keys{"$base\\$key"}}, "'$name' = $value";
|
|
}
|
|
|
|
################################################################
|
|
# add a new registry string value
|
|
sub add_registry_string_value($$$$)
|
|
{
|
|
my ($base, $key, $name, $value) = @_;
|
|
$value =~ s/\'/\'\'/g;
|
|
add_registry_value( $base, $key, $name, "s '$value'" );
|
|
}
|
|
|
|
################################################################
|
|
# add a new registry dword value
|
|
sub add_registry_dword_value($$$$)
|
|
{
|
|
my ($base, $key, $name, $value) = @_;
|
|
add_registry_value( $base, $key, $name, "d $value" );
|
|
}
|
|
|
|
################################################################
|
|
# add a new registry binary value
|
|
sub add_registry_binary_value($$$$)
|
|
{
|
|
my ($base, $key, $name, $value) = @_;
|
|
add_registry_value( $base, $key, $name, "b " . join "", map { sprintf "%02x", $_; } unpack( "C*", $value ));
|
|
}
|
|
|
|
################################################################
|
|
# define a new lead byte
|
|
sub add_lead_byte($)
|
|
{
|
|
my $ch = shift;
|
|
return if defined $cp2uni[$ch];
|
|
push @lead_bytes, $ch;
|
|
$cp2uni[$ch] = 0;
|
|
}
|
|
|
|
################################################################
|
|
# define a new char mapping
|
|
sub add_mapping($$)
|
|
{
|
|
my ($cp, $uni) = @_;
|
|
$cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
|
|
$uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
|
|
if ($cp > 0xff) { add_lead_byte( $cp >> 8 ); }
|
|
}
|
|
|
|
################################################################
|
|
# get a mapping including glyph chars for MB_USEGLYPHCHARS
|
|
sub get_glyphs_mapping(@)
|
|
{
|
|
my @table = @_;
|
|
|
|
for (my $i = 0; $i < @glyph2uni; $i++)
|
|
{
|
|
$table[$i] = $glyph2uni[$i] if defined $glyph2uni[$i];
|
|
}
|
|
return @table;
|
|
}
|
|
|
|
################################################################
|
|
# build EUC-JP table from the JIS 0208/0212 files
|
|
sub dump_eucjp_codepage()
|
|
{
|
|
@cp2uni = ();
|
|
@glyph2uni = ();
|
|
@lead_bytes = ();
|
|
@uni2cp = ();
|
|
$default_char = $DEF_CHAR;
|
|
$default_wchar = 0x30fb;
|
|
|
|
# ASCII chars
|
|
foreach my $i (0x00 .. 0x7f) { add_mapping( $i, $i ); }
|
|
|
|
# lead bytes
|
|
foreach my $i (0x8e, 0xa1 .. 0xfe) { add_lead_byte($i); }
|
|
|
|
# JIS X 0201 right plane
|
|
foreach my $i (0xa1 .. 0xdf) { add_mapping( 0x8e00 + $i, 0xfec0 + $i ); }
|
|
|
|
# undefined chars
|
|
foreach my $i (0x80 .. 0x8d, 0x8f .. 0x9f) { $cp2uni[$i] = $i; }
|
|
$cp2uni[0xa0] = 0xf8f0;
|
|
$cp2uni[0xff] = 0xf8f3;
|
|
|
|
# Fix backslash conversion
|
|
add_mapping( 0xa1c0, 0xff3c );
|
|
|
|
# Add private mappings for rows undefined in JIS 0208/0212
|
|
my $private = 0xe000;
|
|
foreach my $hi (0xf5 .. 0xfe)
|
|
{
|
|
foreach my $lo (0xa1 .. 0xfe)
|
|
{
|
|
add_mapping( ($hi << 8) + $lo, $private++ );
|
|
}
|
|
}
|
|
foreach my $hi (0xf5 .. 0xfe)
|
|
{
|
|
foreach my $lo (0x21 .. 0x7e)
|
|
{
|
|
add_mapping( ($hi << 8) + $lo, $private++ );
|
|
}
|
|
}
|
|
|
|
my $INPUT = open_data_file( "jis0208" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^0x[0-9a-fA-F]+\s+0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
|
|
{
|
|
add_mapping( 0x8080 + hex $1, hex $2 );
|
|
next;
|
|
}
|
|
die "Unrecognized line $_\n";
|
|
}
|
|
close $INPUT;
|
|
|
|
$INPUT = open_data_file( "jis0212" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
|
|
{
|
|
add_mapping( 0x8000 + hex $1, hex $2 );
|
|
next;
|
|
}
|
|
die "Unrecognized line $_\n";
|
|
}
|
|
close $INPUT;
|
|
|
|
output_codepage_file( 20932 );
|
|
}
|
|
|
|
################################################################
|
|
# build Korean Wansung table from the KSX1001 file
|
|
sub dump_krwansung_codepage(@)
|
|
{
|
|
my @cp949 = @_;
|
|
@cp2uni = ();
|
|
@glyph2uni = ();
|
|
@lead_bytes = ();
|
|
@uni2cp = ();
|
|
$default_char = 0x3f;
|
|
$default_wchar = 0x003f;
|
|
|
|
# ASCII and undefined chars
|
|
foreach my $i (0x00 .. 0x9f) { add_mapping( $i, $i ); }
|
|
add_mapping( 0xa0, 0xf8e6 );
|
|
add_mapping( 0xad, 0xf8e7 );
|
|
add_mapping( 0xae, 0xf8e8 );
|
|
add_mapping( 0xaf, 0xf8e9 );
|
|
add_mapping( 0xfe, 0xf8ea );
|
|
add_mapping( 0xff, 0xf8eb );
|
|
|
|
my $INPUT = open_data_file( "ksx1001" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
|
|
{
|
|
add_mapping( 0x8080 + hex $1, hex $2 );
|
|
next;
|
|
}
|
|
die "Unrecognized line $_\n";
|
|
}
|
|
close $INPUT;
|
|
|
|
# get some extra mappings from cp 949
|
|
my @defined_lb;
|
|
map { $defined_lb[$_] = 1; } @lead_bytes;
|
|
foreach my $i (0x0000 .. 0xffff)
|
|
{
|
|
next if ($i >= 0x1100 && $i <= 0x11ff); # range not used in 20949
|
|
next unless defined $cp949[$i];
|
|
if ($cp949[$i] >= 0xff)
|
|
{
|
|
# only add chars for lead bytes that exist in 20949
|
|
my $hi = $cp949[$i] >> 8;
|
|
my $lo = $cp949[$i] & 0xff;
|
|
next unless $defined_lb[$hi];
|
|
next unless $lo >= 0xa1 && $lo <= 0xfe;
|
|
}
|
|
add_mapping( $cp949[$i], $i );
|
|
}
|
|
|
|
output_codepage_file( 20949 );
|
|
}
|
|
|
|
|
|
################################################################
|
|
# dump an array of integers
|
|
sub dump_array($$@)
|
|
{
|
|
my ($bit_width, $default, @array) = @_;
|
|
my $format = sprintf "0x%%0%ux", $bit_width / 4;
|
|
my $i;
|
|
my $ret = " ";
|
|
for ($i = 0; $i < $#array; $i++)
|
|
{
|
|
$ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
|
|
$ret .= (($i % 8) != 7) ? ", " : ",\n ";
|
|
}
|
|
$ret .= sprintf($format, defined $array[$i] ? $array[$i] : $default);
|
|
return $ret;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# dump an SBCS mapping table in binary format
|
|
sub dump_binary_sbcs_table($)
|
|
{
|
|
my $codepage = shift;
|
|
|
|
my @header = ( 13, $codepage, 1, $default_char, $default_wchar, $cp2uni[$default_char], $uni2cp[$default_wchar] );
|
|
my $wc_offset = 256 + 3 + (@glyph2uni ? 256 : 0);
|
|
|
|
print OUTPUT pack "S<*", @header;
|
|
print OUTPUT pack "C12", (0) x 12;
|
|
print OUTPUT pack "S<*", $wc_offset, map { $_ || 0; } @cp2uni[0 .. 255];
|
|
|
|
if (@glyph2uni)
|
|
{
|
|
print OUTPUT pack "S<*", 256, get_glyphs_mapping(@cp2uni[0 .. 255]);
|
|
}
|
|
else
|
|
{
|
|
print OUTPUT pack "S<*", 0;
|
|
}
|
|
|
|
print OUTPUT pack "S<*", 0, 0;
|
|
|
|
print OUTPUT pack "C*", map { defined $_ ? $_ : $default_char; } @uni2cp[0 .. 65535];
|
|
}
|
|
|
|
|
|
################################################################
|
|
# dump a DBCS mapping table in binary format
|
|
sub dump_binary_dbcs_table($)
|
|
{
|
|
my $codepage = shift;
|
|
my @lb_ranges = get_lb_ranges();
|
|
my @header = ( 13, $codepage, 2, $default_char, $default_wchar, $cp2uni[$default_char], $uni2cp[$default_wchar] );
|
|
|
|
my @offsets = (0) x 256;
|
|
my $pos = 0;
|
|
foreach my $i (@lead_bytes)
|
|
{
|
|
$offsets[$i] = ($pos += 256);
|
|
$cp2uni[$i] = 0;
|
|
}
|
|
|
|
my $wc_offset = 256 + 3 + 256 * (1 + scalar @lead_bytes);
|
|
|
|
print OUTPUT pack "S<*", @header;
|
|
print OUTPUT pack "C12", @lb_ranges, 0 x 12;
|
|
print OUTPUT pack "S<*", $wc_offset, map { $_ || 0; } @cp2uni[0 .. 255];
|
|
print OUTPUT pack "S<*", 0, scalar @lb_ranges / 2, @offsets;
|
|
|
|
foreach my $i (@lead_bytes)
|
|
{
|
|
my $base = $i << 8;
|
|
print OUTPUT pack "S<*", map { defined $_ ? $_ : $default_wchar; } @cp2uni[$base .. $base + 255];
|
|
}
|
|
|
|
print OUTPUT pack "S<", 4;
|
|
print OUTPUT pack "S<*", map { defined $_ ? $_ : $default_char; } @uni2cp[0 .. 65535];
|
|
}
|
|
|
|
|
|
################################################################
|
|
# get the list of defined lead byte ranges
|
|
sub get_lb_ranges()
|
|
{
|
|
my @list = ();
|
|
my @ranges = ();
|
|
|
|
foreach my $i (@lead_bytes) { $list[$i] = 1; }
|
|
my $on = 0;
|
|
for (my $i = 0; $i < 256; $i++)
|
|
{
|
|
if ($on)
|
|
{
|
|
if (!defined $list[$i]) { push @ranges, $i-1; $on = 0; }
|
|
}
|
|
else
|
|
{
|
|
if ($list[$i]) { push @ranges, $i; $on = 1; }
|
|
}
|
|
}
|
|
if ($on) { push @ranges, 0xff; }
|
|
return @ranges;
|
|
}
|
|
|
|
################################################################
|
|
# dump the Indic Syllabic Category table
|
|
sub dump_indic($)
|
|
{
|
|
my $filename = shift;
|
|
my @indic_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "IndicSyllabicCategory.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*#/)
|
|
{
|
|
my $type = $2;
|
|
die "unknown indic $type" unless defined $indic_types{$type};
|
|
if (hex $1 < 65536)
|
|
{
|
|
$indic_table[hex $1] = $indic_types{$type};
|
|
}
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown indic $type" unless defined $indic_types{$type};
|
|
if (hex $1 < 65536 and hex $2 < 65536)
|
|
{
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$indic_table[$i] = $indic_types{$type};
|
|
}
|
|
}
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
my $prev_data_file = $current_data_file;
|
|
$INPUT = open_data_file( "ucd", "IndicPositionalCategory.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*#/)
|
|
{
|
|
my $type = $2;
|
|
die "unknown matra $type" unless defined $matra_types{$type};
|
|
$indic_table[hex $1] |= $matra_types{$type} << 8;
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*#/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown matra $type" unless defined $matra_types{$type};
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$indic_table[$i] |= $matra_types{$type} << 8;
|
|
}
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Indic Syllabic Category */\n";
|
|
print OUTPUT "/* generated from $prev_data_file */\n";
|
|
print OUTPUT "/* and from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_two_level_mapping( "indic_syllabic_table", $indic_types{'Other'}, 16, @indic_table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the Line Break Properties table
|
|
sub dump_linebreak($)
|
|
{
|
|
my $filename = shift;
|
|
my @break_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "LineBreak.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z][0-9A-Z])+\s*/)
|
|
{
|
|
my $type = $2;
|
|
die "unknown breaktype $type" unless defined $break_types{$type};
|
|
$break_table[hex $1] = $break_types{$type};
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z][0-9A-Z])+\s*/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown breaktype $type" unless defined $break_types{$type};
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$break_table[$i] = $break_types{$type};
|
|
}
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
|
|
{
|
|
my $type = $2;
|
|
die "unknown breaktype $type" unless defined $break_types{$type};
|
|
$break_table[hex $1] = $break_types{$type};
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([0-9A-Z][0-9A-Z])+\s*/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown breaktype $type" unless defined $break_types{$type};
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$break_table[$i] = $break_types{$type};
|
|
}
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Line Break Properties */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_three_level_mapping( "wine_linebreak_table", $break_types{'XX'}, 16, @break_table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
my %scripts =
|
|
(
|
|
"Unknown" => 0,
|
|
"Common" => 1,
|
|
"Inherited" => 2,
|
|
"Arabic" => 3,
|
|
"Armenian" => 4,
|
|
"Avestan" => 5,
|
|
"Balinese" => 6,
|
|
"Bamum" => 7,
|
|
"Batak" => 8,
|
|
"Bengali" => 9,
|
|
"Bopomofo" => 10,
|
|
"Brahmi" => 11,
|
|
"Braille" => 12,
|
|
"Buginese" => 13,
|
|
"Buhid" => 14,
|
|
"Canadian_Aboriginal" => 15,
|
|
"Carian" => 16,
|
|
"Cham" => 17,
|
|
"Cherokee" => 18,
|
|
"Coptic" => 19,
|
|
"Cuneiform" => 20,
|
|
"Cypriot" => 21,
|
|
"Cyrillic" => 22,
|
|
"Deseret" => 23,
|
|
"Devanagari" => 24,
|
|
"Egyptian_Hieroglyphs" => 25,
|
|
"Ethiopic" => 26,
|
|
"Georgian" => 27,
|
|
"Glagolitic" => 28,
|
|
"Gothic" => 29,
|
|
"Greek" => 30,
|
|
"Gujarati" => 31,
|
|
"Gurmukhi" => 32,
|
|
"Han" => 33,
|
|
"Hangul" => 34,
|
|
"Hanunoo" => 35,
|
|
"Hebrew" => 36,
|
|
"Hiragana" => 37,
|
|
"Imperial_Aramaic" => 38,
|
|
"Inscriptional_Pahlavi" => 39,
|
|
"Inscriptional_Parthian" => 40,
|
|
"Javanese" => 41,
|
|
"Kaithi" => 42,
|
|
"Kannada" => 43,
|
|
"Katakana" => 44,
|
|
"Kayah_Li" => 45,
|
|
"Kharoshthi" => 46,
|
|
"Khmer" => 47,
|
|
"Lao" => 48,
|
|
"Latin" => 49,
|
|
"Lepcha" => 50,
|
|
"Limbu" => 51,
|
|
"Linear_B" => 52,
|
|
"Lisu" => 53,
|
|
"Lycian" => 54,
|
|
"Lydian" => 55,
|
|
"Malayalam" => 56,
|
|
"Mandaic" => 57,
|
|
"Meetei_Mayek" => 58,
|
|
"Mongolian" => 59,
|
|
"Myanmar" => 60,
|
|
"New_Tai_Lue" => 61,
|
|
"Nko" => 62,
|
|
"Ogham" => 63,
|
|
"Ol_Chiki" => 64,
|
|
"Old_Italic" => 65,
|
|
"Old_Persian" => 66,
|
|
"Old_South_Arabian" => 67,
|
|
"Old_Turkic" => 68,
|
|
"Oriya" => 69,
|
|
"Osmanya" => 70,
|
|
"Phags_Pa" => 71,
|
|
"Phoenician" => 72,
|
|
"Rejang" => 73,
|
|
"Runic" => 74,
|
|
"Samaritan" => 75,
|
|
"Saurashtra" => 76,
|
|
"Shavian" => 77,
|
|
"Sinhala" => 78,
|
|
"Sundanese" => 79,
|
|
"Syloti_Nagri" => 80,
|
|
"Syriac" => 81,
|
|
"Tagalog" => 82,
|
|
"Tagbanwa" => 83,
|
|
"Tai_Le" => 84,
|
|
"Tai_Tham" => 85,
|
|
"Tai_Viet" => 86,
|
|
"Tamil" => 87,
|
|
"Telugu" => 88,
|
|
"Thaana" => 89,
|
|
"Thai" => 90,
|
|
"Tibetan" => 91,
|
|
"Tifinagh" => 92,
|
|
"Ugaritic" => 93,
|
|
"Vai" => 94,
|
|
"Yi" => 95,
|
|
# Win8/Win8.1
|
|
"Chakma" => 96,
|
|
"Meroitic_Cursive" => 97,
|
|
"Meroitic_Hieroglyphs" => 98,
|
|
"Miao" => 99,
|
|
"Sharada" => 100,
|
|
"Sora_Sompeng" => 101,
|
|
"Takri" => 102,
|
|
# Win10
|
|
"Bassa_Vah" => 103,
|
|
"Caucasian_Albanian" => 104,
|
|
"Duployan" => 105,
|
|
"Elbasan" => 106,
|
|
"Grantha" => 107,
|
|
"Khojki" => 108,
|
|
"Khudawadi" => 109,
|
|
"Linear_A" => 110,
|
|
"Mahajani" => 111,
|
|
"Manichaean" => 112,
|
|
"Mende_Kikakui" => 113,
|
|
"Modi" => 114,
|
|
"Mro" => 115,
|
|
"Nabataean" => 116,
|
|
"Old_North_Arabian" => 117,
|
|
"Old_Permic" => 118,
|
|
"Pahawh_Hmong" => 119,
|
|
"Palmyrene" => 120,
|
|
"Pau_Cin_Hau" => 121,
|
|
"Psalter_Pahlavi" => 122,
|
|
"Siddham" => 123,
|
|
"Tirhuta" => 124,
|
|
"Warang_Citi" => 125,
|
|
# Win10 RS1
|
|
"Adlam" => 126,
|
|
"Ahom" => 127,
|
|
"Anatolian_Hieroglyphs" => 128,
|
|
"Bhaiksuki" => 129,
|
|
"Hatran" => 130,
|
|
"Marchen" => 131,
|
|
"Multani" => 132,
|
|
"Newa" => 133,
|
|
"Old_Hungarian" => 134,
|
|
"Osage" => 135,
|
|
"SignWriting" => 136,
|
|
"Tangut" => 137,
|
|
# Win10 RS4
|
|
"Masaram_Gondi" => 138,
|
|
"Nushu" => 139,
|
|
"Soyombo" => 140,
|
|
"Zanabazar_Square" => 141,
|
|
# Win10 1903
|
|
"Dogra" => 142,
|
|
"Gunjala_Gondi" => 143,
|
|
"Hanifi_Rohingya" => 144,
|
|
"Makasar" => 145,
|
|
"Medefaidrin" => 146,
|
|
"Old_Sogdian" => 147,
|
|
"Sogdian" => 148,
|
|
# Win10 2004
|
|
"Elymaic" => 149,
|
|
"Nyiakeng_Puachue_Hmong" => 150,
|
|
"Nandinagari" => 151,
|
|
"Wancho" => 152,
|
|
# Win11
|
|
"Chorasmian" => 153,
|
|
"Dives_Akuru" => 154,
|
|
"Khitan_Small_Script" => 155,
|
|
"Yezidi" => 156,
|
|
);
|
|
|
|
################################################################
|
|
# dump Script IDs table
|
|
sub dump_scripts($)
|
|
{
|
|
my $filename = shift;
|
|
my $header = $filename;
|
|
my @scripts_table;
|
|
my $script_index;
|
|
my $i;
|
|
|
|
my $INPUT = open_data_file( "ucd", "Scripts.txt" );
|
|
# Fill the table
|
|
# Unknown script id is always 0, so undefined scripts are automatically treated as such
|
|
while (<$INPUT>)
|
|
{
|
|
my $type = "";
|
|
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*/)
|
|
{
|
|
$type = $2;
|
|
if (defined $scripts{$type})
|
|
{
|
|
$scripts_table[hex $1] = $scripts{$type};
|
|
}
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*/)
|
|
{
|
|
$type = $3;
|
|
if (defined $scripts{$type})
|
|
{
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$scripts_table[$i] = $scripts{$type};
|
|
}
|
|
}
|
|
next;
|
|
}
|
|
}
|
|
|
|
close $INPUT;
|
|
|
|
$header = "$filename.h";
|
|
open OUTPUT,">$header.new" or die "Cannot create $header";
|
|
print "Building $header\n";
|
|
print OUTPUT "/* Unicode Script IDs */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
|
|
print OUTPUT "enum unicode_script_id {\n";
|
|
foreach my $script (sort { $scripts{$a} <=> $scripts{$b} } keys %scripts)
|
|
{
|
|
print OUTPUT " Script_$script = $scripts{$script},\n";
|
|
}
|
|
print OUTPUT " Script_LastId = ", (scalar keys %scripts) - 1, "\n";
|
|
print OUTPUT "};\n";
|
|
|
|
close OUTPUT;
|
|
save_file($header);
|
|
|
|
$filename = "$filename.c";
|
|
open OUTPUT,">$filename.new" or die "Cannot create $header";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Script IDs */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_three_level_mapping( "wine_scripts_table", 0, 16, @scripts_table );
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the BiDi mirroring table
|
|
sub dump_mirroring($)
|
|
{
|
|
my $filename = shift;
|
|
my @mirror_table = ();
|
|
|
|
my $INPUT = open_data_file( "ucd", "BidiMirroring.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9a-fA-F]+)/)
|
|
{
|
|
$mirror_table[hex $1] = hex $2;
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode BiDi mirroring */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
dump_two_level_mapping( "wine_mirror_map", 0, 16, @mirror_table );
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the Bidi Brackets
|
|
sub dump_bracket($)
|
|
{
|
|
my $filename = shift;
|
|
my @bracket_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "BidiBrackets.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([0-9a-fA-F]+);\s*([con])/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown bracket $type" unless defined $bracket_types{$type};
|
|
die "characters too distant $1 and $2" if abs(hex($2) - hex($1)) >= 128;
|
|
$bracket_table[hex $1] = (hex($2) - hex($1)) % 255;
|
|
$bracket_table[hex $1] += $bracket_types{$type} << 8;
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Bidirectional Bracket table */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_two_level_mapping( "bidi_bracket_table", 0, 16, @bracket_table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the Arabic shaping table
|
|
sub dump_shaping($)
|
|
{
|
|
my $filename = shift;
|
|
my @joining_table = @initial_joining_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "ArabicShaping.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;.*;\s*([RLDCUT])\s*;\s*(\w+)/)
|
|
{
|
|
my $type = $2;
|
|
$joining_table[hex $1] = $joining_types{$type};
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Arabic shaping */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_two_level_mapping( "wine_shaping_table", 0, 16, @joining_table );
|
|
|
|
print OUTPUT "\nconst unsigned short wine_shaping_forms[256][4] =\n{\n";
|
|
for (my $i = 0x600; $i <= 0x6ff; $i++)
|
|
{
|
|
printf OUTPUT " { 0x%04x, 0x%04x, 0x%04x, 0x%04x },\n",
|
|
${joining_forms{"isolated"}}[$i] || $i,
|
|
${joining_forms{"final"}}[$i] || $i,
|
|
${joining_forms{"initial"}}[$i] || $i,
|
|
${joining_forms{"medial"}}[$i] || $i;
|
|
}
|
|
print OUTPUT "};\n";
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the Arabic shaping table
|
|
sub dump_arabic_shaping($)
|
|
{
|
|
my $filename = shift;
|
|
my @joining_table = @initial_joining_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "ArabicShaping.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;.*;\s*([RLDCUT])\s*;\s*(\w+)/)
|
|
{
|
|
my $type = $2;
|
|
my $group = $3;
|
|
|
|
if ($group eq "ALAPH" || $group eq "DALATH RISH")
|
|
{
|
|
$joining_table[hex $1] = $joining_types{$group};
|
|
}
|
|
else
|
|
{
|
|
$joining_table[hex $1] = $joining_types{$type};
|
|
}
|
|
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Arabic shaping */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_three_level_mapping( "arabic_shaping_table", 0, 16, @joining_table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# dump the Vertical Orientation table
|
|
sub dump_vertical($$)
|
|
{
|
|
my ($filename, $unix) = @_;
|
|
my @vertical_table;
|
|
|
|
my $INPUT = open_data_file( "ucd", "VerticalOrientation.txt" );
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^\#/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
if (/^\s*([0-9a-fA-F]+)\s*;\s*([a-zA-Z_]+)\s*/)
|
|
{
|
|
my $type = $2;
|
|
die "unknown vertical $type" unless defined $vertical_types{$type};
|
|
if (hex $1 < 65536)
|
|
{
|
|
$vertical_table[hex $1] = $vertical_types{$type};
|
|
}
|
|
next;
|
|
}
|
|
elsif (/^\s*([0-9a-fA-F]+)\.\.\s*([0-9a-fA-F]+)\s*;\s*([A-Za-z_]+)\s*/)
|
|
{
|
|
my $type = $3;
|
|
die "unknown vertical $type" unless defined $vertical_types{$type};
|
|
foreach my $i (hex $1 .. hex $2)
|
|
{
|
|
$vertical_table[$i] = $vertical_types{$type};
|
|
}
|
|
next;
|
|
}
|
|
die "malformed line $_";
|
|
}
|
|
close $INPUT;
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
print OUTPUT "/* Unicode Vertical Orientation */\n";
|
|
print OUTPUT "/* generated from $current_data_file */\n";
|
|
print OUTPUT "/* DO NOT EDIT!! */\n\n";
|
|
if ($unix)
|
|
{
|
|
print OUTPUT "#if 0\n";
|
|
print OUTPUT "#pragma makedep unix\n";
|
|
print OUTPUT "#endif\n\n";
|
|
}
|
|
print OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
dump_two_level_mapping( "vertical_orientation_table", $vertical_types{'R'}, 16, @vertical_table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
################################################################
|
|
# compress a mapping table by removing identical rows
|
|
sub compress_array($$@)
|
|
{
|
|
my $rows = shift;
|
|
my $def = shift;
|
|
my @table = @_;
|
|
my $len = @table / $rows;
|
|
my @array;
|
|
my $data = "";
|
|
|
|
# try to merge table rows
|
|
for (my $row = 0; $row < $rows; $row++)
|
|
{
|
|
my $rowtxt = pack "U*", map { defined($_) ? $_ : $def; } @table[($row * $len)..(($row + 1) * $len - 1)];
|
|
my $pos = index $data, $rowtxt;
|
|
if ($pos == -1)
|
|
{
|
|
# check if the tail of the data can match the start of the new row
|
|
my $first = substr( $rowtxt, 0, 1 );
|
|
for (my $i = length($data) - 1; $i > 0; $i--)
|
|
{
|
|
$pos = index( substr( $data, -$i ), $first );
|
|
last if $pos == -1;
|
|
$i -= $pos;
|
|
next unless substr( $data, -$i ) eq substr( $rowtxt, 0, $i );
|
|
substr( $data, -$i ) = "";
|
|
last;
|
|
}
|
|
$pos = length $data;
|
|
$data .= $rowtxt;
|
|
}
|
|
$array[$row] = $rows + $pos;
|
|
}
|
|
return @array, unpack "U*", $data;
|
|
}
|
|
|
|
################################################################
|
|
# dump a char -> value mapping table using two-level tables
|
|
sub dump_two_level_mapping($$$@)
|
|
{
|
|
my $name = shift;
|
|
my $def = shift;
|
|
my $size = shift;
|
|
my $type = $size == 16 ? "unsigned short" : "unsigned int";
|
|
my (@array, @row_array, @data, @row_data);
|
|
(@row_array[0..4095], @data) = compress_array( 4096, $def, @_[0..65535] );
|
|
(@array[0..255], @row_data) = compress_array( 256, 0, @row_array );
|
|
|
|
for (my $i = 0; $i < @row_data; $i++) { $row_data[$i] += @row_data + 256 - 4096; }
|
|
|
|
printf OUTPUT "const %s %s[%d] =\n{\n", $type, $name, @array + @row_data + @data;
|
|
printf OUTPUT " /* level 1 offsets */\n%s,\n", dump_array( $size, 0, @array );
|
|
printf OUTPUT " /* level 2 offsets */\n%s,\n", dump_array( $size, 0, @row_data );
|
|
printf OUTPUT " /* values */\n%s\n};\n", dump_array( $size, 0, @data );
|
|
}
|
|
|
|
################################################################
|
|
# dump a char -> value mapping table using three-level tables
|
|
sub dump_three_level_mapping($$@)
|
|
{
|
|
my $name = shift;
|
|
my $def = shift;
|
|
my $size = shift;
|
|
my $type = $size == 16 ? "unsigned short" : "unsigned int";
|
|
my $level3 = ($MAX_CHAR + 1) / 16;
|
|
my $level2 = $level3 / 16;
|
|
my $level1 = $level2 / 16;
|
|
my @array3 = compress_array( $level3, $def, @_[0..$MAX_CHAR] );
|
|
my @array2 = compress_array( $level2, 0, @array3[0..$level3-1] );
|
|
my @array1 = compress_array( $level1, 0, @array2[0..$level2-1] );
|
|
|
|
for (my $i = $level2; $i < @array2; $i++) { $array2[$i] += @array1 + @array2 - $level2 - $level3; }
|
|
for (my $i = $level1; $i < @array1; $i++) { $array1[$i] += @array1 - $level2; }
|
|
|
|
printf OUTPUT "const %s %s[%u] =\n{\n", $type, $name, @array1 + (@array2 - $level2) + (@array3 - $level3);
|
|
printf OUTPUT " /* level 1 offsets */\n%s,\n", dump_array( $size, 0, @array1[0..$level1-1] );
|
|
printf OUTPUT " /* level 2 offsets */\n%s,\n", dump_array( $size, 0, @array1[$level1..$#array1] );
|
|
printf OUTPUT " /* level 3 offsets */\n%s,\n", dump_array( $size, 0, @array2[$level2..$#array2] );
|
|
printf OUTPUT " /* values */\n%s\n};\n", dump_array( $size, 0, @array3[$level3..$#array3] );
|
|
}
|
|
|
|
################################################################
|
|
# dump a binary case mapping table in l_intl.nls format
|
|
sub dump_binary_case_table(@)
|
|
{
|
|
my (@table) = @_;
|
|
my @difftable;
|
|
my @res;
|
|
|
|
for (my $i = 0; $i < @table; $i++)
|
|
{
|
|
next unless defined $table[$i];
|
|
$difftable[$i] = ($table[$i] - $i) & 0xffffffff;
|
|
}
|
|
|
|
my (@low_array1, @low_array2, @low_data, @low_row_data);
|
|
(@low_array2[0..4095], @low_data) = compress_array( 4096, 0, @difftable[0..65535] );
|
|
(@low_array1[0..255], @low_row_data) = compress_array( 256, 0, @low_array2 );
|
|
|
|
if (scalar @table > 0x10000)
|
|
{
|
|
my (@high_array1, @high_array2, @high_data, @high_row_data);
|
|
(@high_array2[0..32767], @high_data) = compress_array( 32768, 0, @difftable[65536..$MAX_CHAR] );
|
|
(@high_array1[0..1023], @high_row_data) = compress_array( 1024, 0, @high_array2 );
|
|
|
|
push @res, map { $_ + 1024; } @low_array1;
|
|
push @res, map { $_ + @res + @low_row_data + @low_data; } @high_array1;
|
|
push @res, map { $_ + @res + @low_row_data - 4096; } @low_row_data;
|
|
push @res, @low_data;
|
|
push @res, map { 2 * ($_ - 32768) + @res + @high_row_data; } @high_row_data;
|
|
return pack( "S<*", 1 + scalar @res + 2 * scalar @high_data, @res ) . pack( "L<*", @high_data );
|
|
}
|
|
else
|
|
{
|
|
push @res, @low_array1;
|
|
push @res, map { $_ + @res + @low_row_data - 4096; } @low_row_data;
|
|
push @res, @low_data;
|
|
return pack "S<*", 1 + scalar @res, @res;
|
|
}
|
|
}
|
|
|
|
################################################################
|
|
# dump case mappings for l_intl.nls
|
|
sub dump_intl_nls($)
|
|
{
|
|
my @upper_table = @toupper_table;
|
|
my @lower_table = @tolower_table;
|
|
remove_linguistic_mappings( \@upper_table, \@lower_table );
|
|
|
|
my $upper = dump_binary_case_table( @upper_table[0..65535] );
|
|
my $lower = dump_binary_case_table( @lower_table[0..65535] );
|
|
|
|
my $filename = shift;
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
printf "Building $filename\n";
|
|
|
|
binmode OUTPUT;
|
|
print OUTPUT pack "S<", 1; # version
|
|
print OUTPUT $upper;
|
|
print OUTPUT $lower;
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# dump the bidi direction table
|
|
sub dump_bidi_dir_table($)
|
|
{
|
|
my $filename = shift;
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
printf "Building $filename\n";
|
|
printf OUTPUT "/* Unicode BiDi direction table */\n";
|
|
printf OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
|
|
printf OUTPUT "#include \"windef.h\"\n\n";
|
|
|
|
my @table;
|
|
|
|
for (my $i = 0; $i < @direction_table; $i++)
|
|
{
|
|
$table[$i] = $bidi_types{$direction_table[$i]} if defined $direction_table[$i];
|
|
}
|
|
|
|
dump_three_level_mapping( "bidi_direction_table", $bidi_types{"L"}, 16, @table );
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
|
|
sub rol($$)
|
|
{
|
|
my ($byte, $count) = @_;
|
|
return (($byte << $count) | ($byte >> (8 - $count))) & 0xff;
|
|
}
|
|
|
|
################################################################
|
|
# compress the character properties table
|
|
sub compress_char_props_table($@)
|
|
{
|
|
my $rows = shift;
|
|
my @table = @_;
|
|
my $len = @table / $rows;
|
|
my $pos = 0;
|
|
my @array = (0) x $rows;
|
|
my %sequences;
|
|
|
|
# add some predefined sequences
|
|
foreach my $i (0, 0xfb .. 0xff) { $sequences{pack "L*", (rol($i,5)) x $len} = $i; }
|
|
|
|
# try to merge table rows
|
|
for (my $row = 0; $row < $rows; $row++)
|
|
{
|
|
my @table_row = map { defined $_ ? $_ : 0x7f; } @table[($row * $len)..(($row + 1) * $len - 1)];
|
|
my $rowtxt = pack "L*", @table_row;
|
|
if (defined($sequences{$rowtxt}))
|
|
{
|
|
# reuse an existing row
|
|
$array[$row] = $sequences{$rowtxt};
|
|
}
|
|
else
|
|
{
|
|
# create a new row
|
|
$sequences{$rowtxt} = $array[$row] = ++$pos;
|
|
push @array, @table_row;
|
|
}
|
|
}
|
|
return @array;
|
|
}
|
|
|
|
################################################################
|
|
# dump a normalization table in binary format
|
|
sub dump_norm_table($)
|
|
{
|
|
my $filename = shift;
|
|
|
|
my %forms = ( "nfc" => 1, "nfd" => 2, "nfkc" => 5, "nfkd" => 6, "idna" => 13 );
|
|
my %decomp = ( "nfc" => \@decomp_table,
|
|
"nfd" => \@decomp_table,
|
|
"nfkc" => \@decomp_compat_table,
|
|
"nfkd" => \@decomp_compat_table ,
|
|
"idna" => \@idna_decomp_table );
|
|
|
|
open OUTPUT,">$filename.new" or die "Cannot create $filename";
|
|
print "Building $filename\n";
|
|
|
|
my $type = $filename;
|
|
$type =~ s!.*/norm(\w+)\.nls!$1!;
|
|
|
|
my $compose = $forms{$type} & 1;
|
|
my $compat = !!($forms{$type} & 4) + ($type eq "idna");
|
|
|
|
my @version = split /\./, $UNIVERSION;
|
|
|
|
# combining classes
|
|
|
|
my @classes;
|
|
my @class_values;
|
|
|
|
foreach my $c (grep defined, @combining_class_table)
|
|
{
|
|
$classes[$c] = 1 if $c < 0x100;
|
|
}
|
|
for (my $i = 0; $i < @classes; $i++)
|
|
{
|
|
next unless defined $classes[$i];
|
|
$classes[$i] = @class_values;
|
|
push @class_values, $i;
|
|
}
|
|
push @class_values, 0 if (@class_values % 2);
|
|
die "too many classes" if @class_values >= 0x40;
|
|
|
|
# character properties
|
|
|
|
my @char_props;
|
|
my @decomposed;
|
|
my @comp_hash_table;
|
|
my $comp_hash_size = $compose ? 254 : 0;
|
|
|
|
for (my $i = 0; $i <= $MAX_CHAR; $i++)
|
|
{
|
|
next unless defined $combining_class_table[$i];
|
|
if (defined $decomp{$type}->[$i])
|
|
{
|
|
my @dec = get_decomposition( $i, $decomp{$type} );
|
|
if ($compose && (my @comp = get_composition( $i, $compat )))
|
|
{
|
|
my $hash = ($comp[0] + 95 * $comp[1]) % $comp_hash_size;
|
|
push @{$comp_hash_table[$hash]}, to_utf16( @comp, $i );
|
|
|
|
my $val = 0;
|
|
foreach my $d (@dec)
|
|
{
|
|
$val = $combining_class_table[$d];
|
|
last if $val;
|
|
}
|
|
$char_props[$i] = $classes[$val];
|
|
}
|
|
else
|
|
{
|
|
$char_props[$i] = 0xbf;
|
|
}
|
|
@dec = compose_hangul( @dec ) if $compose;
|
|
@dec = to_utf16( @dec );
|
|
push @dec, 0 if @dec >= 7;
|
|
$decomposed[$i] = \@dec;
|
|
}
|
|
else
|
|
{
|
|
if ($combining_class_table[$i] == 0x100)
|
|
{
|
|
$char_props[$i] = 0x7f;
|
|
}
|
|
elsif ($combining_class_table[$i])
|
|
{
|
|
$char_props[$i] = $classes[$combining_class_table[$i]] | 0x80;
|
|
}
|
|
elsif ($type eq "idna" && defined $idna_disallowed[$i])
|
|
{
|
|
$char_props[$i] = 0xff;
|
|
}
|
|
else
|
|
{
|
|
$char_props[$i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($compose)
|
|
{
|
|
for (my $i = 0; $i <= $MAX_CHAR; $i++)
|
|
{
|
|
my @comp = get_composition( $i, $compat );
|
|
next unless @comp;
|
|
if ($combining_class_table[$comp[1]])
|
|
{
|
|
$char_props[$comp[0]] |= 0x40 unless $char_props[$comp[0]] & 0x80;
|
|
$char_props[$comp[1]] |= 0x40;
|
|
}
|
|
else
|
|
{
|
|
$char_props[$comp[0]] = ($char_props[$comp[0]] & ~0x40) | 0x80;
|
|
$char_props[$comp[1]] |= 0xc0;
|
|
}
|
|
}
|
|
}
|
|
|
|
# surrogates
|
|
foreach my $i (0xd800..0xdbff) { $char_props[$i] = 0xdf; }
|
|
foreach my $i (0xdc00..0xdfff) { $char_props[$i] = 0x9f; }
|
|
|
|
# Hangul
|
|
if ($type eq "nfc") { foreach my $i (0x1100..0x117f) { $char_props[$i] = 0xff; } }
|
|
elsif ($compose) { foreach my $i (0x1100..0x11ff) { $char_props[$i] = 0xff; } }
|
|
foreach my $i (0xac00..0xd7ff) { $char_props[$i] = 0xff; }
|
|
|
|
# invalid chars
|
|
if ($type eq "idna") { foreach my $i (0x00..0x1f, 0x7f) { $char_props[$i] = 0xff; } }
|
|
foreach my $i (0xfdd0..0xfdef) { $char_props[$i] = 0xff; }
|
|
foreach my $i (0x00..0x10)
|
|
{
|
|
$char_props[($i << 16) | 0xfffe] = 0xff;
|
|
$char_props[($i << 16) | 0xffff] = 0xff;
|
|
}
|
|
|
|
# decomposition hash table
|
|
|
|
my @decomp_hash_table;
|
|
my @decomp_hash_index;
|
|
my @decomp_hash_data;
|
|
my $decomp_hash_size = 944;
|
|
|
|
# build string of character data, reusing substrings when possible
|
|
my $decomp_char_data = "";
|
|
foreach my $i (sort { @{$b} <=> @{$a} } grep defined, @decomposed)
|
|
{
|
|
my $str = pack "U*", @{$i};
|
|
$decomp_char_data .= $str if index( $decomp_char_data, $str) == -1;
|
|
}
|
|
for (my $i = 0; $i < @decomposed; $i++)
|
|
{
|
|
next unless defined $decomposed[$i];
|
|
my $pos = index( $decomp_char_data, pack( "U*", @{$decomposed[$i]} ));
|
|
die "sequence not found" if $pos == -1;
|
|
my $len = @{$decomposed[$i]};
|
|
$len = 7 if $len > 7;
|
|
my $hash = $i % $decomp_hash_size;
|
|
push @{$decomp_hash_table[$hash]}, [ $i, ($len << 13) | $pos ];
|
|
}
|
|
for (my $i = 0; $i < $decomp_hash_size; $i++)
|
|
{
|
|
$decomp_hash_index[$i] = @decomp_hash_data / 2;
|
|
next unless defined $decomp_hash_table[$i];
|
|
if (@{$decomp_hash_table[$i]} == 1)
|
|
{
|
|
my $entry = $decomp_hash_table[$i]->[0];
|
|
if ($char_props[$entry->[0]] == 0xbf)
|
|
{
|
|
$decomp_hash_index[$i] = $entry->[1];
|
|
next;
|
|
}
|
|
}
|
|
foreach my $entry (@{$decomp_hash_table[$i]})
|
|
{
|
|
push @decomp_hash_data, $entry->[0] & 0xffff, $entry->[1];
|
|
}
|
|
}
|
|
push @decomp_hash_data, 0, 0;
|
|
|
|
# composition hash table
|
|
|
|
my @comp_hash_index;
|
|
my @comp_hash_data;
|
|
if (@comp_hash_table)
|
|
{
|
|
for (my $i = 0; $i < $comp_hash_size; $i++)
|
|
{
|
|
$comp_hash_index[$i] = @comp_hash_data;
|
|
push @comp_hash_data, @{$comp_hash_table[$i]} if defined $comp_hash_table[$i];
|
|
}
|
|
$comp_hash_index[$comp_hash_size] = @comp_hash_data;
|
|
push @comp_hash_data, 0, 0, 0;
|
|
}
|
|
|
|
my $level1 = ($MAX_CHAR + 1) / 128;
|
|
my @rows = compress_char_props_table( $level1, @char_props[0..$MAX_CHAR] );
|
|
|
|
my @header = ( $version[0], $version[1], $version[2], 0, $forms{$type}, $compat ? 18 : 3,
|
|
0, $decomp_hash_size, $comp_hash_size, 0 );
|
|
my @tables = (0) x 8;
|
|
|
|
$tables[0] = 16 + @header + @tables;
|
|
$tables[1] = $tables[0] + @class_values / 2;
|
|
$tables[2] = $tables[1] + $level1 / 2;
|
|
$tables[3] = $tables[2] + (@rows - $level1) / 2;
|
|
$tables[4] = $tables[3] + @decomp_hash_index;
|
|
$tables[5] = $tables[4] + @decomp_hash_data;
|
|
$tables[6] = $tables[5] + length $decomp_char_data;
|
|
$tables[7] = $tables[6] + @comp_hash_index;
|
|
|
|
print OUTPUT pack "S<16", unpack "U*", "norm$type.nlp";
|
|
print OUTPUT pack "S<*", @header;
|
|
print OUTPUT pack "S<*", @tables;
|
|
print OUTPUT pack "C*", @class_values;
|
|
|
|
print OUTPUT pack "C*", @rows[0..$level1-1];
|
|
print OUTPUT pack "C*", @rows[$level1..$#rows];
|
|
print OUTPUT pack "S<*", @decomp_hash_index;
|
|
print OUTPUT pack "S<*", @decomp_hash_data;
|
|
print OUTPUT pack "S<*", unpack "U*", $decomp_char_data;
|
|
print OUTPUT pack "S<*", @comp_hash_index;
|
|
print OUTPUT pack "S<*", @comp_hash_data;
|
|
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
|
|
add_registry_string_value( $nlskey, "Normalization", sprintf( "%x", $forms{$type} ), "norm$type.nls" );
|
|
}
|
|
|
|
|
|
################################################################
|
|
# output a codepage definition file from the global tables
|
|
sub output_codepage_file($)
|
|
{
|
|
my $codepage = shift;
|
|
|
|
my $output = sprintf "nls/c_%03d.nls", $codepage;
|
|
open OUTPUT,">$output.new" or die "Cannot create $output";
|
|
|
|
printf "Building %s\n", $output;
|
|
if (!@lead_bytes) { dump_binary_sbcs_table( $codepage ); }
|
|
else { dump_binary_dbcs_table( $codepage ); }
|
|
|
|
close OUTPUT;
|
|
save_file($output);
|
|
|
|
add_registry_string_value( $nlskey, "Codepage", sprintf( "%d", $codepage ), sprintf( "c_%03d.nls", $codepage ));
|
|
}
|
|
|
|
################################################################
|
|
# output a codepage table from a Microsoft-style mapping file
|
|
sub dump_msdata_codepage($)
|
|
{
|
|
my $filename = shift;
|
|
|
|
my $state = "";
|
|
my ($codepage, $width, $count);
|
|
my ($lb_cur, $lb_end);
|
|
|
|
@cp2uni = ();
|
|
@glyph2uni = ();
|
|
@lead_bytes = ();
|
|
@uni2cp = ();
|
|
$default_char = $DEF_CHAR;
|
|
$default_wchar = $DEF_CHAR;
|
|
|
|
my $INPUT = open_data_file( "codepages", $filename );
|
|
|
|
while (<$INPUT>)
|
|
{
|
|
next if /^;/; # skip comments
|
|
next if /^\s*$/; # skip empty lines
|
|
next if /\x1a/; # skip ^Z
|
|
last if /^ENDCODEPAGE/;
|
|
|
|
if (/^CODEPAGE\s+(\d+)/)
|
|
{
|
|
$codepage = $1;
|
|
next;
|
|
}
|
|
if (/^CPINFO\s+(\d+)\s+0x([0-9a-fA-f]+)\s+0x([0-9a-fA-F]+)/)
|
|
{
|
|
$width = $1;
|
|
$default_char = hex $2;
|
|
$default_wchar = hex $3;
|
|
next;
|
|
}
|
|
if (/^(MBTABLE|GLYPHTABLE|WCTABLE|DBCSRANGE|DBCSTABLE)\s+(\d+)/)
|
|
{
|
|
$state = $1;
|
|
$count = $2;
|
|
next;
|
|
}
|
|
if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)/)
|
|
{
|
|
if ($state eq "MBTABLE")
|
|
{
|
|
my $cp = hex $1;
|
|
my $uni = hex $2;
|
|
$cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
|
|
next;
|
|
}
|
|
if ($state eq "GLYPHTABLE")
|
|
{
|
|
my $cp = hex $1;
|
|
my $uni = hex $2;
|
|
$glyph2uni[$cp] = $uni unless defined($glyph2uni[$cp]);
|
|
next;
|
|
}
|
|
if ($state eq "WCTABLE")
|
|
{
|
|
my $uni = hex $1;
|
|
my $cp = hex $2;
|
|
$uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
|
|
next;
|
|
}
|
|
if ($state eq "DBCSRANGE")
|
|
{
|
|
my $start = hex $1;
|
|
my $end = hex $2;
|
|
for (my $i = $start; $i <= $end; $i++) { add_lead_byte( $i ); }
|
|
$lb_cur = $start;
|
|
$lb_end = $end;
|
|
next;
|
|
}
|
|
if ($state eq "DBCSTABLE")
|
|
{
|
|
my $mb = hex $1;
|
|
my $uni = hex $2;
|
|
my $cp = ($lb_cur << 8) | $mb;
|
|
$cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
|
|
if (!--$count)
|
|
{
|
|
if (++$lb_cur > $lb_end) { $state = "DBCSRANGE"; }
|
|
}
|
|
next;
|
|
}
|
|
}
|
|
die "$filename: Unrecognized line $_\n";
|
|
}
|
|
close $INPUT;
|
|
|
|
output_codepage_file( $codepage );
|
|
|
|
if ($codepage == 949) { dump_krwansung_codepage( @uni2cp ); }
|
|
}
|
|
|
|
################################################################
|
|
# align a string length
|
|
sub align_string($$)
|
|
{
|
|
my ($align, $str) = @_;
|
|
$str .= pack "C*", (0) x ($align - length($str) % $align) if length($str) % $align;
|
|
return $str;
|
|
}
|
|
|
|
################################################################
|
|
# pad a string with zeros
|
|
sub pad_string($$)
|
|
{
|
|
my ($pad, $str) = @_;
|
|
$str .= pack "C*", (0) x ($pad - length($str)) if length($str) < $pad;
|
|
return $str;
|
|
}
|
|
|
|
################################################################
|
|
# pack a GUID string
|
|
sub pack_guid($)
|
|
{
|
|
$_ = shift;
|
|
/([0-9A-Fa-f]{8})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})-([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})/;
|
|
return pack "L<S<2C8", hex $1, hex $2, hex $3, hex $4, hex $5, hex $6, hex $7, hex $8, hex $9, hex $10, hex $11;
|
|
}
|
|
|
|
################################################################
|
|
# comparison function for compression sort
|
|
sub cmp_compression
|
|
{
|
|
return scalar @{$a} <=> scalar @{$b} ||
|
|
$a->[4] <=> $b->[4] ||
|
|
$a->[5] <=> $b->[5] ||
|
|
$a->[6] <=> $b->[6] ||
|
|
$a->[7] <=> $b->[7] ||
|
|
$a->[8] <=> $b->[8] ||
|
|
$a->[9] <=> $b->[9] ||
|
|
$a->[10] <=> $b->[10] ||
|
|
$a->[11] <=> $b->[11] ||
|
|
$a->[12] <=> $b->[12];
|
|
}
|
|
|
|
################################################################
|
|
# build a binary sort keys table
|
|
sub dump_sortkey_table($)
|
|
{
|
|
my $filename = shift;
|
|
my @keys;
|
|
my ($part, $section, $subsection, $guid, $version, $ling_flag);
|
|
my @multiple_weights;
|
|
my @expansions;
|
|
my @compressions;
|
|
my %exceptions;
|
|
my %guids;
|
|
my %compr_flags;
|
|
my %locales;
|
|
my $default_guid = "00000001-57ee-1e5c-00b4-d0000bb1e11e";
|
|
my $jamostr = "";
|
|
|
|
my $re_hex = '0x[0-9A-Fa-f]+';
|
|
my $re_key = '(\d+\s+\d+\s+\d+\s+\d+)';
|
|
$guids{$default_guid} = { };
|
|
|
|
my %flags = ( "HAS_3_BYTE_WEIGHTS" => 0x01, "REVERSEDIACRITICS" => 0x10, "DOUBLECOMPRESSION" => 0x20, "INVERSECASING" => 0x40 );
|
|
|
|
my $KEYS = open_data_file( "sorting" );
|
|
|
|
printf "Building $filename\n";
|
|
|
|
while (<$KEYS>)
|
|
{
|
|
s/\s*;.*$//;
|
|
next if /^\s*$/; # skip empty lines
|
|
if (/^\s*(SORTKEY|SORTTABLES)/)
|
|
{
|
|
$part = $1;
|
|
next;
|
|
}
|
|
if (/^\s*(ENDSORTKEY|ENDSORTTABLES)/)
|
|
{
|
|
$part = $section = "";
|
|
next;
|
|
}
|
|
if (/^\s*(DEFAULT|RELEASE|REVERSEDIACRITICS|DOUBLECOMPRESSION|INVERSECASING|MULTIPLEWEIGHTS|EXPANSION|COMPATIBILITY|COMPRESSION|EXCEPTION|JAMOSORT)\s+/)
|
|
{
|
|
$section = $1;
|
|
$guid = undef;
|
|
next;
|
|
}
|
|
next unless $part;
|
|
if ("$part.$section" eq "SORTKEY.DEFAULT")
|
|
{
|
|
if (/^\s*($re_hex)\s+$re_key/)
|
|
{
|
|
$keys[hex $1] = [ split(/\s+/,$2) ];
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.RELEASE")
|
|
{
|
|
if (/^\s*NLSVERSION\s+0x([0-9A-Fa-f]+)/)
|
|
{
|
|
$version = hex $1;
|
|
next;
|
|
}
|
|
if (/^\s*DEFINEDVERSION\s+0x([0-9A-Fa-f]+)/)
|
|
{
|
|
# ignore for now
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.REVERSEDIACRITICS" ||
|
|
"$part.$section" eq "SORTTABLES.DOUBLECOMPRESSION" ||
|
|
"$part.$section" eq "SORTTABLES.INVERSECASING")
|
|
{
|
|
if (/^\s*SORTGUID\s+([-0-9A-Fa-f]+)/)
|
|
{
|
|
$guid = lc $1;
|
|
$guids{$guid} = { } unless defined $guids{$guid};
|
|
$guids{$guid}->{flags} |= $flags{$section};
|
|
next;
|
|
}
|
|
if (/^\s*LOCALENAME\s+([A-Za-z0-9-_]+)/)
|
|
{
|
|
$locales{$1} = $guid;
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.MULTIPLEWEIGHTS")
|
|
{
|
|
if (/^\s*(\d+)\s+(\d+)/)
|
|
{
|
|
push @multiple_weights, $1, $2;
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.EXPANSION")
|
|
{
|
|
if (/^\s*0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)/)
|
|
{
|
|
my $pos = scalar @expansions / 2;
|
|
$keys[hex $1] = [ 2, 0, $pos & 0xff, $pos >> 8 ] unless defined $keys[hex $1];
|
|
push @expansions, hex $2, hex $3;
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.COMPATIBILITY")
|
|
{
|
|
if (/^\s*0x([0-9A-Fa-f]+)\s+0x([0-9A-Fa-f]+)/)
|
|
{
|
|
$keys[hex $1] = $keys[hex $2];
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.COMPRESSION")
|
|
{
|
|
if (/^\s*SORTGUID\s+([-0-9A-Fa-f]+)\s+\d*\s*([A-Z0-9_]+)?/)
|
|
{
|
|
if ($subsection || !$guid) # start a new one
|
|
{
|
|
$guid = lc $1;
|
|
$subsection = "";
|
|
$guids{$guid} = { } unless defined $guids{$guid};
|
|
$guids{$guid}->{flags} |= $flags{$2} if $2;
|
|
$guids{$guid}->{compr} = @compressions;
|
|
$exceptions{"$guid-"} = [ ] unless defined $exceptions{"$guid-"};
|
|
$compr_flags{$guid} = [ ] unless defined $compr_flags{$guid};
|
|
push @compressions, [ ];
|
|
}
|
|
else # merge with current one
|
|
{
|
|
$guids{lc $1} = { } unless defined $guids{lc $1};
|
|
$guids{lc $1}->{flags} |= $flags{$2} if $2;
|
|
$guids{lc $1}->{compr} = $guids{$guid}->{compr};
|
|
$compr_flags{lc $1} = $compr_flags{$guid};
|
|
}
|
|
next;
|
|
}
|
|
if (/^\s*LOCALENAME\s+([A-Za-z0-9-_]+)/)
|
|
{
|
|
$locales{$1} = $guid;
|
|
next;
|
|
}
|
|
if (/^\s*(TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT)/)
|
|
{
|
|
$subsection = $1;
|
|
next;
|
|
}
|
|
if ($subsection && /^\s*(($re_hex\s+){2,8})$re_key/)
|
|
{
|
|
my @comp = map { hex $_; } split(/\s+/,$1);
|
|
push @{$compressions[$#compressions]}, [ split(/\s+/,$3), @comp ];
|
|
# add compression flags
|
|
$compr_flags{$guid}->[$comp[0]] |= @comp >= 6 ? 0xc0 : @comp >= 4 ? 0x80 : 0x40;
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.EXCEPTION")
|
|
{
|
|
if (/^\s*SORTGUID\s+([-0-9A-Fa-f]+)\s+\d*\s*(LINGUISTIC_CASING)?/)
|
|
{
|
|
$guid = lc $1;
|
|
$guids{$guid} = { } unless defined $guids{lc $1};
|
|
$ling_flag = ($2 ? "+" : "-");
|
|
$exceptions{"$guid$ling_flag"} = [ ] unless defined $exceptions{"$guid$ling_flag"};
|
|
next;
|
|
}
|
|
if (/^\s*LOCALENAME\s+([A-Za-z0-9-_]+)/)
|
|
{
|
|
$locales{$1} = $guid;
|
|
next;
|
|
}
|
|
if (/^\s*($re_hex)\s+$re_key/)
|
|
{
|
|
$exceptions{"$guid$ling_flag"}->[hex $1] = [ split(/\s+/,$2) ];
|
|
next;
|
|
}
|
|
}
|
|
elsif ("$part.$section" eq "SORTTABLES.JAMOSORT")
|
|
{
|
|
if (/^\s*$re_hex\s+(($re_hex\s*){5})/)
|
|
{
|
|
$jamostr .= pack "C8", map { hex $_; } split /\s+/, $1;
|
|
next;
|
|
}
|
|
}
|
|
die "$current_data_file: $part.$section: unrecognized line $_\n";
|
|
}
|
|
close $KEYS;
|
|
|
|
# Sortkey table
|
|
|
|
my $table;
|
|
for (my $i = 0; $i < 0x10000; $i++)
|
|
{
|
|
my @k = defined $keys[$i] ? @{$keys[$i]} : (0) x 4;
|
|
$table .= pack "C4", $k[1], $k[0], $k[2], $k[3];
|
|
}
|
|
|
|
foreach my $id (sort keys %exceptions)
|
|
{
|
|
my $pos = length($table) / 4;
|
|
my @exc = @{$exceptions{$id}};
|
|
my @filled;
|
|
my $key = (substr( $id, -1 ) eq "+" ? "ling_except" : "except");
|
|
my $guid = substr( $id, 0, -1 );
|
|
$guids{$guid}->{$key} = $pos;
|
|
$pos += 0x100;
|
|
my @flags = @{$compr_flags{$guid}} if defined $compr_flags{$guid};
|
|
for (my $j = 0; $j < 0x10000; $j++)
|
|
{
|
|
next unless defined $exc[$j] || defined $flags[$j];
|
|
$filled[$j >> 8] = 1;
|
|
$j |= 0xff;
|
|
}
|
|
for (my $j = 0; $j < 0x100; $j++)
|
|
{
|
|
$table .= pack "L<", $filled[$j] ? $pos : $j * 0x100;
|
|
$pos += 0x100 if $filled[$j];
|
|
}
|
|
for (my $j = 0; $j < 0x10000; $j++)
|
|
{
|
|
next unless $filled[$j >> 8];
|
|
my @k = defined $exc[$j] ? @{$exc[$j]} : defined $keys[$j] ? @{$keys[$j]} : (0) x 4;
|
|
$k[3] |= $flags[$j] || 0;
|
|
$table .= pack "C4", $k[1], $k[0], $k[2], $k[3];
|
|
}
|
|
}
|
|
|
|
# Case mapping tables
|
|
|
|
# standard table
|
|
my @casemaps;
|
|
my @upper = @toupper_table;
|
|
my @lower = @tolower_table;
|
|
remove_linguistic_mappings( \@upper, \@lower );
|
|
$casemaps[0] = pack( "S<*", 1) . dump_binary_case_table( @upper ) . dump_binary_case_table( @lower );
|
|
|
|
# linguistic table
|
|
$casemaps[1] = pack( "S<*", 1) . dump_binary_case_table( @toupper_table ) . dump_binary_case_table( @tolower_table );
|
|
|
|
# Turkish table
|
|
@upper = @toupper_table;
|
|
@lower = @tolower_table;
|
|
$upper[ord 'i'] = 0x130; # LATIN CAPITAL LETTER I WITH DOT ABOVE
|
|
$lower[ord 'I'] = 0x131; # LATIN SMALL LETTER DOTLESS I
|
|
$casemaps[2] = pack( "S<*", 1) . dump_binary_case_table( @upper ) . dump_binary_case_table( @lower );
|
|
my $casemaps = align_string( 8, $casemaps[0] . $casemaps[1] . $casemaps[2] );
|
|
|
|
# Char type table
|
|
|
|
my @table;
|
|
my $types = "";
|
|
my %typestr;
|
|
for (my $i = 0; $i < 0x10000; $i++)
|
|
{
|
|
my $str = pack "S<3",
|
|
($category_table[$i] || 0) & 0xffff,
|
|
defined($direction_table[$i]) ? $c2_types{$direction_table[$i]} : 0,
|
|
($category_table[$i] || 0) >> 16;
|
|
|
|
if (!defined($typestr{$str}))
|
|
{
|
|
$typestr{$str} = length($types) / 6;
|
|
$types .= $str;
|
|
}
|
|
$table[$i] = $typestr{$str};
|
|
}
|
|
|
|
my (@rows, @array, @data, @row_data);
|
|
(@rows[0..4095], @data) = compress_array( 4096, 0, @table[0..65535] );
|
|
(@array[0..255], @row_data) = compress_array( 256, 0, @rows );
|
|
for (my $i = 0; $i < 256; $i++) { $array[$i] *= 2; } # we need byte offsets
|
|
for (my $i = 0; $i < @row_data; $i++) { $row_data[$i] += 2 * @row_data + 512 - 4096; }
|
|
|
|
my $arraystr = pack("S<*", @array, @row_data) . pack("C*", @data);
|
|
my $chartypes = pack "S<2", 4 + length($types) + length($arraystr), 2 + length($types);
|
|
$chartypes = align_string( 8, $chartypes . $types . $arraystr );
|
|
|
|
# Sort tables
|
|
|
|
# guids
|
|
my $sorttables = pack "L<2", $version, scalar %guids;
|
|
foreach my $id (sort keys %guids)
|
|
{
|
|
my %guid = %{$guids{$id}};
|
|
my $flags = $guid{flags} || 0;
|
|
my $map = length($casemaps[0]) + (defined $guid{ling_except} ? length($casemaps[1]) : 0);
|
|
$sorttables .= pack_guid($id) . pack "L<5",
|
|
$flags,
|
|
defined($guid{compr}) ? $guid{compr} : 0xffffffff,
|
|
$guid{except} || 0,
|
|
$guid{ling_except} || 0,
|
|
$map / 2;
|
|
}
|
|
|
|
# expansions
|
|
$sorttables .= pack "L<S<*", scalar @expansions / 2, @expansions;
|
|
|
|
# compressions
|
|
$sorttables .= pack "L<", scalar @compressions;
|
|
my $rowstr = "";
|
|
foreach my $c (@compressions)
|
|
{
|
|
my $pos = length($rowstr) / 2;
|
|
my $min = 0xffff;
|
|
my $max = 0;
|
|
my @lengths = (0) x 8;
|
|
foreach my $r (sort cmp_compression @{$c})
|
|
{
|
|
my @row = @{$r};
|
|
$lengths[scalar @row - 6]++;
|
|
foreach my $val (@row[4..$#row])
|
|
{
|
|
$min = $val if $min > $val;
|
|
$max = $val if $max < $val;
|
|
}
|
|
$rowstr .= align_string( 4, pack "S<*", @row[4..$#row] );
|
|
$rowstr .= pack "C4", $row[1], $row[0], $row[2], $row[3];
|
|
}
|
|
$sorttables .= pack "L<S<10", $pos, $min, $max, @lengths;
|
|
}
|
|
$sorttables .= $rowstr;
|
|
|
|
# multiple weights
|
|
$sorttables .= align_string( 4, pack "L<C*", scalar @multiple_weights / 2, @multiple_weights );
|
|
|
|
# jamo sort
|
|
$sorttables .= pack("L<", length($jamostr) / 8) . $jamostr;
|
|
|
|
# Locales
|
|
|
|
add_registry_key( $nlskey, "Sorting\\Ids", "{$default_guid}" );
|
|
foreach my $loc (sort keys %locales)
|
|
{
|
|
# skip specific locales that match more general ones
|
|
my @parts = split /[-_]/, $loc;
|
|
next if @parts > 1 && defined($locales{$parts[0]}) && $locales{$parts[0]} eq $locales{$loc};
|
|
next if @parts > 2 && defined($locales{"$parts[0]-$parts[1]"}) && $locales{"$parts[0]-$parts[1]"} eq $locales{$loc};
|
|
add_registry_string_value( $nlskey, "Sorting\\Ids", $loc, "\{$locales{$loc}\}" );
|
|
}
|
|
|
|
# File header
|
|
|
|
my @header;
|
|
$header[0] = 16;
|
|
$header[1] = $header[0] + length $table;
|
|
$header[2] = $header[1] + length $casemaps;
|
|
$header[3] = $header[2] + length $chartypes;
|
|
|
|
open OUTPUT, ">$filename.new" or die "Cannot create $filename";
|
|
print OUTPUT pack "L<*", @header;
|
|
print OUTPUT $table, $casemaps, $chartypes, $sorttables;
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
return $chartypes;
|
|
}
|
|
|
|
|
|
my %lcnames;
|
|
|
|
sub locale_parent($)
|
|
{
|
|
my $loc = shift;
|
|
|
|
return undef unless $loc;
|
|
return $lcnames{$loc}->{sparent} if defined $lcnames{$loc} && defined $lcnames{$loc}->{sparent};
|
|
return $lcnames{$loc}->{parent} if defined $lcnames{$loc} && defined $lcnames{$loc}->{parent};
|
|
if ($loc =~ /(.*)-[0-9A-Za-z]+/) { return $1; }
|
|
return "";
|
|
}
|
|
|
|
sub compare_locales
|
|
{
|
|
(my $n1 = $a) =~ tr/A-Z_/a-z-/;
|
|
(my $n2 = $b) =~ tr/A-Z_/a-z-/;
|
|
return $n1 cmp $n2;
|
|
}
|
|
|
|
# query an xml key
|
|
sub xml_query($$)
|
|
{
|
|
my ($xml, $query) = @_;
|
|
my $ret = $xml->find( $query );
|
|
return undef unless $ret;
|
|
printf STDERR "multiple entries for %s\n", $query if (@{$ret} > 1);
|
|
return @{$ret}[0]->textContent;
|
|
}
|
|
|
|
# query an xml key for a locale, with fallback to the parents
|
|
sub loc_query($$)
|
|
{
|
|
my ($loc, $query) = @_;
|
|
|
|
$loc = $lcnames{"en-US"} unless $loc->{name}; # fallback to "en-US" for root locale
|
|
|
|
for (my $cur = $loc->{name}; defined $cur; $cur = locale_parent( $cur ))
|
|
{
|
|
next unless defined $lcnames{$cur};
|
|
my $xml = $lcnames{$cur}->{xml};
|
|
my $ret = $xml->find( $query );
|
|
next unless $ret;
|
|
printf STDERR "%s: multiple entries for %s\n", $cur, $query if (@{$ret} > 1);
|
|
next if @{$ret}[0]->textContent eq "\x{2191}\x{2191}\x{2191}"; # "↑↑↑"
|
|
return @{$ret}[0]->textContent;
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# retrieve a locale field entry by going up the parents tree
|
|
sub locale_entry($$$)
|
|
{
|
|
my ($loc, $field, $def) = @_;
|
|
|
|
return $loc->{$field} if defined $loc->{$field};
|
|
|
|
unless ($loc->{name}) # fallback to "en-US" for root locale
|
|
{
|
|
$loc = $lcnames{"en-US"};
|
|
return $loc->{$field} if defined $loc->{$field};
|
|
}
|
|
while (defined $loc->{alias}) # resolve aliases
|
|
{
|
|
$loc = $lcnames{$loc->{alias}};
|
|
return $loc->{$field} if defined $loc->{$field};
|
|
}
|
|
my $cur = $loc->{name};
|
|
while ($cur)
|
|
{
|
|
if (defined $lcnames{$cur} && defined $lcnames{$cur}->{sparent})
|
|
{
|
|
$cur = $lcnames{$cur}->{sparent};
|
|
}
|
|
elsif ($cur =~ /(.*)-[0-9A-Za-z]+/)
|
|
{
|
|
$cur = $1;
|
|
}
|
|
else
|
|
{
|
|
return $def;
|
|
}
|
|
return $lcnames{$cur}->{$field} if defined $lcnames{$cur} && defined $lcnames{$cur}->{$field};
|
|
}
|
|
return $def;
|
|
}
|
|
|
|
my $string_data;
|
|
|
|
sub add_str_data($)
|
|
{
|
|
my $txt = shift;
|
|
my $ret = index( $string_data, $txt );
|
|
if ($ret == -1)
|
|
{
|
|
$ret = length($string_data);
|
|
$string_data .= $txt
|
|
}
|
|
return $ret / 2;
|
|
}
|
|
|
|
sub add_string($)
|
|
{
|
|
my $str = shift;
|
|
return 0 unless defined($str) && $str ne "";
|
|
my $utf = encode( "UTF16LE", $str );
|
|
return add_str_data( (pack "S<", length($utf) / 2) . $utf . (pack "S", 0) );
|
|
}
|
|
|
|
sub add_fontsig(@)
|
|
{
|
|
return add_str_data( pack "S<L<*", scalar(@_) * 2, @_ );
|
|
}
|
|
|
|
sub add_strarray(@)
|
|
{
|
|
return 0 unless @_;
|
|
return add_str_data( pack "S<L<*", scalar @_, map { add_string($_) } @_);
|
|
}
|
|
|
|
sub format_to_grouping($)
|
|
{
|
|
my $format = shift;
|
|
if ($format =~ /#,(#+),(#+0)/) { return chr(length($2)) . chr(length($1)); }
|
|
if ($format =~ /#,(#+0)/) { return chr(length($1)); }
|
|
# printf STDERR "unknown format %s\n", $format;
|
|
return chr(3);
|
|
}
|
|
|
|
sub parse_currency_format($$)
|
|
{
|
|
my $name = shift;
|
|
my ($posfmt, $negfmt) = split /;/, shift;
|
|
my @pospatterns = ( "\xa4[^\xa0]*#", # $1.1
|
|
"00[^\xa0]*\xa4", # 1.1$
|
|
"\xa4.*\xa0.*#", # $ 1.1
|
|
"00.*\xa0.*\xa4" ); # 1.1 $
|
|
my @negpatterns = ( "\\(\xa4[^\xa0]*#", # ($1.1)
|
|
"-\xa4[^\xa0]*#", # -$1.1
|
|
"\xa4[^\xa0]*-#", # $-1.1
|
|
"\xa4[^\xa0]*#.*00-", # $1.1-
|
|
"00[^\xa0]*\xa4\\)", # (1.1$)
|
|
"-#.*00[^\xa0]*\xa4", # -1.1$
|
|
"00-[^\xa0]*\xa4", # 1.1-$
|
|
"00[^\xa0]*\xa4-", # 1.1$-
|
|
"-#.*00.*\xa0.*\xa4", # -1.1 $
|
|
"-\xa4.*\xa0.*#", # -$ 1.1
|
|
"00.*\xa0.*\xa4-", # 1.1 $-
|
|
"\xa4.*\xa0.*#.*00-", # $ 1.1-
|
|
"\xa4.*\xa0.*-#", # $ -1.1
|
|
"00-.*\xa0.*\xa4", # 1.1- $
|
|
"\\(\xa4.*\xa0.*#", # ($ 1.1)
|
|
"00.*\xa0.*\xa4\\)"); # (1.1 $)
|
|
my ($pos, $neg);
|
|
|
|
for ($pos = 0; $pos < @pospatterns; $pos++)
|
|
{
|
|
last if ($posfmt =~ /$pospatterns[$pos]/);
|
|
}
|
|
#printf STDERR "$name: unknown format '%s'\n", $posfmt if ($pos == @pospatterns);
|
|
$pos = 0 if ($pos == @pospatterns);
|
|
|
|
if (defined $negfmt)
|
|
{
|
|
for ($neg = 0; $neg < @negpatterns; $neg++)
|
|
{
|
|
last if ($negfmt =~ /$negpatterns[$neg]/);
|
|
}
|
|
#printf STDERR "$name: unknown format '%s'\n", $negfmt if ($neg == @negpatterns);
|
|
$neg = 0 if ($neg == @negpatterns);
|
|
}
|
|
elsif ($pos == 0) { $neg = 1; }
|
|
elsif ($pos == 1) { $neg = 5; }
|
|
elsif ($pos == 2) { $neg = 9; }
|
|
elsif ($pos == 3) { $neg = 8; }
|
|
|
|
return ($pos, $neg);
|
|
}
|
|
|
|
sub parse_percent_format($)
|
|
{
|
|
my $fmt = shift;
|
|
my @patterns = ( "0.+%", # 1 %
|
|
"0%", # 1%
|
|
"%#", # %1
|
|
"%.+#" ); # % 1
|
|
my $pos;
|
|
for ($pos = 0; $pos < @patterns; $pos++)
|
|
{
|
|
last if ($fmt =~ /$patterns[$pos]/);
|
|
}
|
|
printf STDERR "unknown format '%s'\n", $fmt if ($pos == @patterns);
|
|
return ($pos, ($pos == 3) ? 7 : $pos);
|
|
}
|
|
|
|
sub convert_date_format($)
|
|
{
|
|
my $fmt = shift;
|
|
$fmt =~ s/G+/gg/;
|
|
$fmt =~ s/LLLL/MMMM/;
|
|
$fmt =~ s/LLL/MMM/;
|
|
$fmt =~ s/E+/dddd/;
|
|
$fmt =~ s/ccc+/dddd/;
|
|
$fmt =~ s/([^gy])y([^y])/$1yyyy$2/;
|
|
$fmt =~ s/^y([^y])/yyyy$1/;
|
|
$fmt =~ s/([^gy])y$/$1yyyy/;
|
|
return $fmt;
|
|
}
|
|
|
|
sub convert_time_format($)
|
|
{
|
|
my $fmt = shift;
|
|
$fmt =~ s/a+/tt/;
|
|
$fmt =~ s/B+/tt/;
|
|
$fmt =~ s/\x{202f}/ /;
|
|
return $fmt;
|
|
}
|
|
|
|
sub load_iso639()
|
|
{
|
|
my %iso639;
|
|
my $DATA = open_data_file( "iso639", "iso-639-3_Code_Tables_$ISO639VERSION/iso-639-3_$ISO639VERSION.tab" );
|
|
while (<$DATA>)
|
|
{
|
|
if (/^\s*[a-z]{3}\s+[a-z]{3}\s+([a-z]{3})\s+([a-z]{2})\s/) { $iso639{$2} = $1; }
|
|
}
|
|
close $DATA;
|
|
return %iso639;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build the locale table for locale.nls
|
|
sub build_locale_data()
|
|
{
|
|
my $base = "cldr-release-$CLDRVERSION";
|
|
my $suppl = load_xml_data_file( "cldr", "$base/common/supplemental/supplementalData.xml" );
|
|
my $subtags = load_xml_data_file( "cldr", "$base/common/supplemental/likelySubtags.xml" );
|
|
my $numbers = load_xml_data_file( "cldr", "$base/common/supplemental/numberingSystems.xml" );
|
|
# obsolete phone data from CLDR version 33
|
|
my $phone = load_xml_data_file( "cldr33", "common/supplemental/telephoneCodeData.xml" );
|
|
my %iso639 = load_iso639();
|
|
$string_data = pack "S2", 0, 0; # offset 0 == empty string
|
|
|
|
%lcnames = map { $_->{name} => $_ } @locales;
|
|
|
|
my %lcids;
|
|
foreach my $loc (@locales) { $lcids{$loc->{lcid}} = $loc if defined $loc->{lcid}; }
|
|
|
|
my %days = ( "sun" => 0, "mon" => 1, "tue" => 2, "wed" => 3, "thu" => 4, "fri" => 5, "sat" => 6 );
|
|
|
|
# assign locale parents
|
|
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if $loc->{name} eq "";
|
|
next if defined $loc->{parent};
|
|
(my $unix_name = $loc->{name}) =~ s/-/_/g;
|
|
my $parent = xml_query( $suppl, "/supplementalData/parentLocales[not(\@component)]/parentLocale[contains(concat(' ',\@locales,' '),' $unix_name ')]/\@parent" );
|
|
if ($parent)
|
|
{
|
|
$parent =~ s/_/-/g;
|
|
$parent = "" if $parent eq "root";
|
|
}
|
|
elsif ($loc->{name} =~ /(.*)-[0-9A-Za-z]+/) { $parent = $1; }
|
|
$loc->{parent} = $parent || "";
|
|
}
|
|
|
|
# load per-locale XML files
|
|
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if defined $loc->{alias};
|
|
(my $file = $loc->{file} || $loc->{name}) =~ s/-/_/g;
|
|
$file = "$base/" . ($loc->{dir} || "common") . "/main/$file.xml";
|
|
my $xml = load_xml_data_file( "cldr", $file );
|
|
$loc->{xml} = $xml;
|
|
$loc->{language} ||= xml_query( $xml, "/ldml/identity/language/\@type" );
|
|
$loc->{territory} ||= xml_query( $xml, "/ldml/identity/territory/\@type" );
|
|
$loc->{script} = xml_query( $xml, "/ldml/identity/script/\@type" );
|
|
if (!defined($loc->{territory}) && $loc->{name} =~ /-([A-Z]{2}|[0-9]{3})$/) { $loc->{territory} = $1; }
|
|
if (!defined($loc->{script}) && $loc->{name} =~ /-([A-Z][a-z]{3})(-[A-Z]{2})?$/) { $loc->{script} = $1; }
|
|
}
|
|
|
|
# assign a default territory and sort locale
|
|
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if defined $loc->{alias};
|
|
next if defined $loc->{territory};
|
|
my $id = $loc->{sortlocale};
|
|
if (defined $id && ($id =~ /[-_]([A-Z0-9]+)$/))
|
|
{
|
|
$loc->{territory} = $1;
|
|
next;
|
|
}
|
|
my @children = grep /^$loc->{name}-[A-Z0-9]+$/ && !defined $lcnames{$_}->{alias}, keys %lcnames;
|
|
if (@children == 1)
|
|
{
|
|
$id = $children[0];
|
|
}
|
|
else
|
|
{
|
|
my $name = $loc->{file} || $loc->{name};
|
|
$name =~ s/-(Arab|Beng|Cyrl|Deva|Guru|Hans|Hant|Latn|Tfng|Vaii)$//;
|
|
$name =~ s/-/_/g;
|
|
$id = xml_query( $subtags, "/supplementalData/likelySubtags/likelySubtag[\@from='$name']/\@to" );
|
|
$id =~ s/_/-/g if $id;
|
|
}
|
|
if ($id =~ /[-_]([A-Z0-9]+)$/)
|
|
{
|
|
$loc->{territory} = $1;
|
|
next if defined $loc->{sortlocale};
|
|
next unless $id =~ /^$loc->{name}/;
|
|
while (defined $lcnames{$id} && defined $lcnames{$id}->{alias}) { $id = $lcnames{$id}->{alias}; }
|
|
$loc->{sortlocale} = $id if defined $lcnames{$id};
|
|
next;
|
|
}
|
|
print STDERR "no territory found for $loc->{name}\n";
|
|
}
|
|
|
|
# fill geoid table
|
|
|
|
my %geotable;
|
|
foreach my $geo (@geoids)
|
|
{
|
|
my $name = $geo->{name};
|
|
next unless defined $name;
|
|
$geo->{alias} = $geotable{$name} if defined $geotable{$name};
|
|
$geotable{$name} ||= $geo;
|
|
}
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if defined $loc->{alias};
|
|
my $territory = $loc->{territory};
|
|
$geotable{$territory} ||= { name => $territory };
|
|
}
|
|
foreach my $name (keys %geotable)
|
|
{
|
|
my $geo = $geotable{$name};
|
|
$geo->{dialcode} = xml_query( $phone, "(/supplementalData/telephoneCodeData/codesByTerritory[\@territory='$name']/telephoneCountryCode)[1]/\@code" );
|
|
if ($name =~ /\d+/)
|
|
{
|
|
$geo->{uncode} = $name;
|
|
next;
|
|
}
|
|
$geo->{iso2} = $name;
|
|
$geo->{iso3} = xml_query( $suppl, "/supplementalData/codeMappings/territoryCodes[\@type='$name']/\@alpha3");
|
|
$geo->{uncode} = xml_query( $suppl, "/supplementalData/codeMappings/territoryCodes[\@type='$name']/\@numeric");
|
|
$geo->{sintlsymbol} ||= xml_query( $suppl, "(/supplementalData/currencyData/region[\@iso3166='$name']/currency[not(\@to)])[1]/\@iso4217") || "XXX";
|
|
$geo->{sintlsymbol} =~ s/XXX/XDR/;
|
|
}
|
|
foreach my $geo (@geoids)
|
|
{
|
|
$geo->{parentid} = $geotable{$geo->{parent}}->{id} if defined $geo->{parent};
|
|
next if defined $geo->{iso2};
|
|
next if defined $geo->{alias};
|
|
next unless defined $geo->{uncode};
|
|
my @contains;
|
|
my $list = xml_query( $suppl, "/supplementalData/territoryContainment/group[\@type='$geo->{uncode}' and not(\@status)]/\@contains");
|
|
push @contains, split /\s+/, $list if defined $list;
|
|
$list = xml_query( $suppl, "/supplementalData/territoryContainment/group[\@type='$geo->{uncode}' and \@status='deprecated']/\@contains");
|
|
push @contains, split /\s+/, $list if defined $list;
|
|
while (@contains)
|
|
{
|
|
my $territory = pop @contains;
|
|
if (defined $geotable{$territory})
|
|
{
|
|
$geotable{$territory}->{parentid} ||= $geo->{id};
|
|
}
|
|
elsif ($territory =~ /\d+/)
|
|
{
|
|
# expand region recursively
|
|
$list = xml_query( $suppl, "/supplementalData/territoryContainment/group[\@type='$territory' and not(\@status)]/\@contains" );
|
|
push @contains, split /\s+/, $list if defined $list;
|
|
}
|
|
}
|
|
}
|
|
|
|
# assign calendars to their locale
|
|
|
|
foreach my $cal (@calendars)
|
|
{
|
|
next unless defined $cal->{locale};
|
|
my $loc = $lcnames{$cal->{locale}};
|
|
$loc->{calendar} = [ ] unless defined $loc->{calendar};
|
|
push @{$loc->{calendar}}, $cal;
|
|
}
|
|
|
|
# assign default lcid to aliases
|
|
|
|
foreach my $loc (@locales)
|
|
{
|
|
next unless defined $loc->{alias};
|
|
next if defined $loc->{lcid};
|
|
my $alias = $loc->{alias};
|
|
my $lcid = $lcnames{$alias}->{lcid} || 0x1000;
|
|
$loc->{lcid} = $lcid | 0x80000000;
|
|
}
|
|
|
|
# assign sort aliases to parent locale
|
|
|
|
foreach my $loc (@locales)
|
|
{
|
|
next unless $loc->{name} =~ /_/;
|
|
next unless defined $loc->{alias};
|
|
my $alias = $loc->{alias};
|
|
my $parent = $lcnames{$alias};
|
|
my $basename = $parent->{name};
|
|
while (1)
|
|
{
|
|
@{$parent->{sortnames}}[($loc->{lcid} >> 16) - 1] = $loc->{name};
|
|
$alias = locale_parent( $alias );
|
|
last unless $alias && defined $lcnames{$alias};
|
|
$parent = $lcnames{$alias};
|
|
last if defined $parent->{sortbase} && $parent->{sortbase} ne $basename;
|
|
$parent->{sortbase} = $basename;
|
|
}
|
|
}
|
|
|
|
# assign an array index to all locales
|
|
|
|
my $idx = 0;
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if defined $loc->{alias};
|
|
$loc->{idx} = $idx++;
|
|
}
|
|
foreach my $loc (@locales)
|
|
{
|
|
my $alias = $loc->{alias};
|
|
next unless defined $alias;
|
|
while (defined $lcnames{$alias}->{alias}) { $alias = $lcnames{$alias}->{alias}; }
|
|
$loc->{idx} = $lcnames{$alias}->{idx};
|
|
}
|
|
|
|
# output lcids table
|
|
|
|
my $lcid_data = "";
|
|
foreach my $id (sort { $a <=> $b } keys %lcids)
|
|
{
|
|
my $loc = $lcids{$id};
|
|
$lcid_data .= pack "L<S<2", $id, $loc->{idx}, add_string($loc->{name});
|
|
}
|
|
|
|
# output lcnames table
|
|
|
|
my $lcname_data = "";
|
|
foreach my $name (sort compare_locales keys %lcnames)
|
|
{
|
|
my $loc = $lcnames{$name};
|
|
$lcname_data .= pack "S<2L<", add_string($name), $loc->{idx}, $loc->{lcid} || 0x1000;
|
|
}
|
|
|
|
# output locales array
|
|
|
|
my $locale_data = "";
|
|
my $default_lcid = 0x8001;
|
|
foreach my $loc (@locales)
|
|
{
|
|
next if defined $loc->{alias};
|
|
my $sname = $loc->{name};
|
|
my $language = $loc->{language};
|
|
my $territory = $loc->{territory};
|
|
my $script = $loc->{script};
|
|
my $neutral = ($sname && $sname !~ /-$territory/);
|
|
my $sparent = $loc->{sparent} || (($sname =~ /(.*)-[0-9A-Za-z]+/) ? $1 : $loc->{parent});
|
|
my $unique_lcid = $loc->{lcid};
|
|
unless (defined $unique_lcid) { $unique_lcid = $default_lcid++; }
|
|
my $geo = $geotable{$territory};
|
|
my $territory_match = "contains(concat(' ',normalize-space(\@territories),' '),' $territory ')";
|
|
|
|
# languages and scripts
|
|
|
|
my $ssortlocale = $loc->{sortlocale} || ($neutral ? "$sname-$territory" : $sname);
|
|
my $idefaultlanguage = defined $lcnames{$ssortlocale} ? $lcnames{$ssortlocale}->{lcid} : undef;
|
|
$idefaultlanguage = $lcnames{"en-US"}->{lcid} unless $ssortlocale;
|
|
(my $siso639langname = $sname) =~ s/-.*$//;
|
|
my $siso639langname2 = $iso639{$siso639langname} || $siso639langname;
|
|
my $sopentypelang = sprintf "%-4s", locale_entry( $loc, "sopentypelang", uc $siso639langname2 );
|
|
my $sabbrevlangname = defined $loc->{lcid} ? locale_entry( $loc, "sabbrevlangname", uc $siso639langname2 ) : "ZZZ";
|
|
my $siso3166ctryname2 = $geo->{iso3} || $geo->{uncode};
|
|
my $senglanguage = loc_query( $lcnames{en}, "/ldml/localeDisplayNames/languages/language[\@type='$language' and not(\@alt)]" ) || "";
|
|
my $sengcountry = loc_query( $lcnames{en}, "/ldml/localeDisplayNames/territories/territory[\@type='$territory' and not(\@alt)]" ) || "";
|
|
my $snativelangname = loc_query( $loc, "/ldml/localeDisplayNames/languages/language[\@type='$language' and not(\@alt)]" );
|
|
my $snativectryname = loc_query( $loc, "/ldml/localeDisplayNames/territories/territory[\@type='$territory' and not(\@alt)]" );
|
|
$sengcountry =~ s/South Korea/Korea/;
|
|
$sengcountry =~ s/T\xfcrkiye/Turkey/;
|
|
$snativelangname ||= $senglanguage;
|
|
$snativectryname ||= $sengcountry;
|
|
if ($script)
|
|
{
|
|
my $engscript = loc_query( $lcnames{en}, "/ldml/localeDisplayNames/scripts/script[\@type='$script' and not(\@alt)]" );
|
|
my $nativescript = loc_query( $loc, "/ldml/localeDisplayNames/scripts/script[\@type='$script' and not(\@alt)]" );
|
|
$senglanguage .= " ($engscript)" if $engscript;
|
|
$snativelangname .= " ($nativescript)" if $nativescript;
|
|
}
|
|
my $sengdisplayname = $neutral ? $senglanguage : "$senglanguage ($sengcountry)";
|
|
my $snativedisplayname = $neutral ? $snativelangname : "$snativelangname ($snativectryname)";
|
|
$sengdisplayname =~ s/\) \(/, /;
|
|
$snativedisplayname =~ s/\) \(/, /;
|
|
my $sscripts = locale_entry( $loc, "sscripts", $script ) || xml_query( $suppl, "/supplementalData/languageData/language[\@type='$language' and not(\@alt)]/\@scripts" );
|
|
$sscripts = (join ";", (sort split / /, ($sscripts || "Latn"))) . ";";
|
|
my $ireadinglayout = locale_entry( $loc, "ireadinglayout", 0 );
|
|
my $charlayout = loc_query( $loc, "/ldml/layout/orientation/characterOrder" );
|
|
if ($charlayout eq "right-to-left")
|
|
{
|
|
$ireadinglayout = 1;
|
|
}
|
|
elsif ($charlayout eq "top-to-bottom")
|
|
{
|
|
my $linelayout = loc_query( $loc, "/ldml/layout/orientation/lineOrder" );
|
|
$ireadinglayout = $linelayout eq "right-to-left" ? 2 : 3;
|
|
}
|
|
my $igeoid = $geo->{id} || 0;
|
|
|
|
# numbers
|
|
|
|
my $sdecimal = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/decimal" );
|
|
my $slist = locale_entry( $loc, "slist", ";" );
|
|
my $smondecimalsep = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/currencyDecimal" ) || $sdecimal;
|
|
my $sthousand = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/group" );
|
|
$sthousand =~ s/\x{202f}/\x{00a0}/;
|
|
my $smonthousandsep = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/currencyGroup" ) || $sthousand;
|
|
my $spositivesign = "";
|
|
my $snegativesign = "-";
|
|
my $spercent = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/percentSign" );
|
|
my $snan = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/nan" );
|
|
my $sposinfinity = loc_query( $loc, "/ldml/numbers/symbols[\@numberSystem='latn']/infinity" );
|
|
my $sneginfinity = $sposinfinity ? "-$sposinfinity" : "";
|
|
my $sgrouping = format_to_grouping( loc_query( $loc, "/ldml/numbers/decimalFormats[\@numberSystem='latn']/decimalFormatLength[not(\@type)]/decimalFormat/pattern" ));
|
|
my $percentformat = loc_query( $loc, "/ldml/numbers/percentFormats[\@numberSystem='latn']/percentFormatLength[not(\@type)]/percentFormat/pattern" );
|
|
my $currencyformat = loc_query( $loc, "/ldml/numbers/currencyFormats[\@numberSystem='latn']/currencyFormatLength[not(\@type)]/currencyFormat[\@type='accounting']/pattern[not(\@alt)]" ) ||
|
|
loc_query( $loc, "/ldml/numbers/currencyFormats[\@numberSystem='latn']/currencyFormatLength[not(\@type)]/currencyFormat[\@type='standard']/pattern[not(\@alt)]" );
|
|
my $smongrouping = format_to_grouping( $currencyformat );
|
|
my ($icurrency, $inegcurr) = parse_currency_format( $sname, $currencyformat );
|
|
my ($ipospercent, $inegpercent) = parse_percent_format( $percentformat );
|
|
my $native_numbering = loc_query( $loc, "/ldml/numbers/otherNumberingSystems/native" );
|
|
my @snativedigits = split //, (locale_entry( $loc, "nativedigits", "" ) || xml_query( $numbers, "/supplementalData/numberingSystems/numberingSystem[\@id='$native_numbering']/\@digits" ));
|
|
my $digitsubstitution = !(ord($snativedigits[0]) >= 0x600 && ord($snativedigits[0]) <= 0x6ff);
|
|
my $measure = defined xml_query( $suppl, "/supplementalData/measurementData/measurementSystem[\@type='US' and $territory_match]" );
|
|
my $papersize = defined xml_query( $suppl, "/supplementalData/measurementData/paperSize[\@type='US-Letter' and $territory_match]" );
|
|
|
|
# currencies
|
|
|
|
my $sintlsymbol = $geo->{sintlsymbol} || "XDR";
|
|
my $scurrency = $geo->{scurrency} || loc_query( $loc, "/ldml/numbers/currencies/currency[\@type='$sintlsymbol']/symbol[\@alt='narrow']" );
|
|
$scurrency ||= loc_query( $loc, "/ldml/numbers/currencies/currency[\@type='$sintlsymbol']/symbol[not(\@alt)]" );
|
|
$scurrency ||= $geo->{sintlsymbol};
|
|
$geo->{scurrency} = $scurrency if $scurrency;
|
|
my $sengcurrname = $loc->{sengcurrname} || loc_query( $lcnames{en}, "/ldml/numbers/currencies/currency[\@type='$sintlsymbol']/displayName[not(\@count)]" );
|
|
my $snativecurrname = $loc->{sengcurrname} || loc_query( $loc, "/ldml/numbers/currencies/currency[\@type='$sintlsymbol']/displayName[not(\@count)]" ) || $sengcurrname;
|
|
my $icurrdigits = xml_query( $suppl, "/supplementalData/currencyData/fractions/info[\@iso4217='$sintlsymbol']/\@digits" );
|
|
$icurrdigits = 2 unless defined $icurrdigits;
|
|
|
|
# calendars
|
|
|
|
my $firstday = xml_query( $suppl, "/supplementalData/weekData/firstDay[not(\@alt) and $territory_match]/\@day" );
|
|
my $ifirstdayofweek = $firstday ? $days{$firstday} : 1;
|
|
my $firstweekofyear = (xml_query( $suppl, "/supplementalData/weekData/minDays[$territory_match]/\@count" ) || 0) == 4 ? 2 : 0;
|
|
my $serastring = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/eras/eraAbbr/era[\@type='1' and not(\@alt)]" );
|
|
my (@sdayname, @sabbrevdayname, @sshortestdayname);
|
|
foreach my $d (sort { $days{$a} <=> $days{$b} } keys %days)
|
|
{
|
|
my $n = $days{$d};
|
|
my %name;
|
|
foreach my $type (qw(wide abbreviated short))
|
|
{
|
|
$name{$type} = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/days/dayContext[\@type='format']/dayWidth[\@type='$type']/day[\@type='$d' and not(\@alt)]" );
|
|
}
|
|
push @sdayname, $name{wide};
|
|
push @sabbrevdayname, $name{abbreviated} || $name{wide};
|
|
push @sshortestdayname, $name{short} || $name{abbreviated} || $name{wide};
|
|
}
|
|
my (@smonthname, @sabbrevmonthname, @sgenitivemonth, @sabbrevgenitivemonth);
|
|
foreach my $n (1..13)
|
|
{
|
|
my $name = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/months/monthContext[\@type='stand-alone']/monthWidth[\@type='wide']/month[\@type='$n']" );
|
|
my $abbrev = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/months/monthContext[\@type='stand-alone']/monthWidth[\@type='abbreviated']/month[\@type='$n']" );
|
|
my $genitive = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/months/monthContext[\@type='format']/monthWidth[\@type='wide']/month[\@type='$n']" );
|
|
my $abbrevgen = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/months/monthContext[\@type='format']/monthWidth[\@type='abbreviated']/month[\@type='$n']" );
|
|
push @smonthname, $name || $genitive || "";
|
|
push @sabbrevmonthname, $abbrev || $abbrevgen || $name || $genitive || "";
|
|
push @sgenitivemonth, $genitive || "";
|
|
push @sabbrevgenitivemonth, $abbrevgen || $genitive || "";
|
|
}
|
|
@sgenitivemonth = () if join("|",@smonthname) eq join("|",@sgenitivemonth);
|
|
@sabbrevgenitivemonth = () if join("|",@sabbrevmonthname) eq join("|",@sabbrevgenitivemonth);
|
|
my %caltypes = ( "gregorian" => 1, "japanese" => 3, "chinese" => 4, "dangi" => 5, "islamic" => 6, "buddhist" => 7, "hebrew" => 8,
|
|
"persian" => 22, "islamic-civil" => 23, "islamic-umalqura" => 23 );
|
|
my $calpref = xml_query( $suppl, "/supplementalData/calendarPreferenceData/calendarPreference[$territory_match]/\@ordering" ) || "gregorian";
|
|
my $icalendartype;
|
|
my @scalnames;
|
|
foreach my $c (split /\s+/, $calpref)
|
|
{
|
|
next unless defined $caltypes{$c};
|
|
$icalendartype .= chr($caltypes{$c});
|
|
$scalnames[$caltypes{$c} - 1] = loc_query( $loc, "/ldml/localeDisplayNames/types/type[\@key='calendar' and \@type='$c']" );
|
|
}
|
|
|
|
# date/time formats
|
|
|
|
my $s1159 = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dayPeriods/dayPeriodContext[\@type='format']/dayPeriodWidth[\@type='abbreviated']/dayPeriod[\@type='am' and not(\@alt)]" );
|
|
my $s2359 = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dayPeriods/dayPeriodContext[\@type='format']/dayPeriodWidth[\@type='abbreviated']/dayPeriod[\@type='pm' and not (\@alt)]" );
|
|
my $sshortestam = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dayPeriods/dayPeriodContext[\@type='format']/dayPeriodWidth[\@type='narrow']/dayPeriod[\@type='am' and not(\@alt)]" );
|
|
my $sshortestpm = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dayPeriods/dayPeriodContext[\@type='format']/dayPeriodWidth[\@type='narrow']/dayPeriod[\@type='pm' and not (\@alt)]" );
|
|
my @stimeformat = (loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/timeFormats/timeFormatLength[\@type='medium']/timeFormat/pattern[not(\@alt)]" ));
|
|
push @stimeformat, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='Hms' and not(\@alt)]" );
|
|
pop @stimeformat if $stimeformat[0] eq $stimeformat[1];
|
|
@stimeformat = map convert_time_format($_), @stimeformat;
|
|
my @sshorttime = (loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/timeFormats/timeFormatLength[\@type='short']/timeFormat/pattern[not(\@alt)]" ));
|
|
push @sshorttime, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='Hm' and not(\@alt)]" );
|
|
pop @sshorttime if $sshorttime[0] eq $sshorttime[1];
|
|
@sshorttime = map convert_time_format($_), @sshorttime;
|
|
my @sshortdate = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yMd' and not(\@alt)]" );
|
|
push @sshortdate, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yMMMd' and not(\@alt)]" );
|
|
@sshortdate = map convert_date_format($_), @sshortdate;
|
|
my @slongdate = (loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateFormats/dateFormatLength[\@type='full']/dateFormat/pattern[not(\@alt)]" ));
|
|
push @slongdate, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateFormats/dateFormatLength[\@type='long']/dateFormat/pattern[not(\@alt)]" );
|
|
@slongdate = map convert_date_format($_), @slongdate;
|
|
my @smonthday = (loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='MMMMd' and not(\@alt)]" ));
|
|
push @smonthday, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='Md' and not(\@alt)]" );
|
|
push @smonthday, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='MMMd' and not(\@alt)]" );
|
|
@smonthday = map convert_date_format($_), @smonthday;
|
|
my @syearmonth = map convert_date_format($_), loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yMMMM' and not(\@alt)]" );
|
|
my @sduration = map convert_time_format( lc $_ ), loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='Hms' and not(\@alt)]" );
|
|
my $srelativelongdate = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='MMMMEd' and not(\@alt)]" ) ||
|
|
loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='gregorian']/dateTimeFormats/availableFormats/dateFormatItem[\@id='MMMEd' and not(\@alt)]" );
|
|
$srelativelongdate = convert_date_format( $srelativelongdate );
|
|
|
|
if (defined $loc->{calendar})
|
|
{
|
|
foreach my $cal (@{$loc->{calendar}})
|
|
{
|
|
$cal->{sshortdate} = \@sshortdate;
|
|
$cal->{syearmonth} = \@syearmonth;
|
|
$cal->{slongdate} = \@slongdate;
|
|
$cal->{serastring} = [ $serastring ];
|
|
$cal->{sdayname} = \@sdayname;
|
|
$cal->{sabbrevdayname} = \@sabbrevdayname;
|
|
$cal->{smonthname} = \@smonthname;
|
|
$cal->{sabbrevmonthname} = \@sabbrevmonthname;
|
|
$cal->{scalname} = $scalnames[$cal->{id}];
|
|
$cal->{smonthday} = \@smonthday;
|
|
$cal->{sshortestdayname} = \@sshortestdayname;
|
|
$cal->{sabbreverastring} = [ $serastring ];
|
|
$cal->{sshortestdayname} = \@sshortestdayname;
|
|
$cal->{srelativelongdate} = $srelativelongdate;
|
|
}
|
|
}
|
|
|
|
# codepages
|
|
|
|
my %ansicpmap = ( 437 => 1252, 720 => 1256, 737 => 1253, 775 => 1257, 850 => 1252,
|
|
852 => 1250, 855 => 1251, 866 => 1251, 857 => 1254, 862 => 1255 );
|
|
my %maccpmap = ( 437 => 10000, 720 => 10004, 737 => 10006, 775 => 10029, 850 => 10000,
|
|
852 => 10029, 855 => 10007, 857 => 10081, 862 => 10005, 866 => 10007,
|
|
874 => 10021, 932 => 10001, 936 => 10008, 949 => 10003, 950 => 10002,
|
|
1258 => 10000 );
|
|
my %ebcdiccpmap = ( 437 => 37, 720 => 20420, 737 => 20273, 866 => 20880, 932 => 20290 );
|
|
my %codepagemasks = ( 874 => [ 0x01000000, 0x00000000, 0x00000000, 0, 0x00010000, 0x00000000, 0x00010000, 0x00000000 ],
|
|
932 => [ 0x00000000, 0x28c70000, 0x00000010, 0, 0x00020000, 0x00000000, 0x00020000, 0x00000000 ],
|
|
936 => [ 0x00000000, 0x28010000, 0x00000002, 0, 0x00040000, 0x00000000, 0x00040000, 0x00000000 ],
|
|
949 => [ 0x00000000, 0x00000000, 0x00000000, 0, 0x00080000, 0x00000000, 0x00080000, 0x00000000 ],
|
|
950 => [ 0x00000000, 0x28c10000, 0x00000012, 0, 0x00100000, 0x00000000, 0x00100000, 0x00000000 ],
|
|
1258 => [ 0x2000000f, 0x00000000, 0x00000000, 0, 0x00000100, 0x00008000, 0x00000100, 0x00008000 ],
|
|
866 => [ 0x00000200, 0x00000000, 0x00000000, 0, 0x00000004, 0x00020000, 0x00000004, 0x02020000 ],
|
|
862 => [ 0x00000800, 0x40000000, 0x00000000, 0, 0x00000020, 0x00200000, 0x00000020, 0x00200000 ],
|
|
857 => [ 0x0000001f, 0x00000000, 0x00000000, 0, 0x00000010, 0x01000000, 0x00000010, 0x01000000 ],
|
|
855 => [ 0x00000200, 0x00000000, 0x00000000, 0, 0x00000004, 0x02000000, 0x00000004, 0x02000000 ],
|
|
852 => [ 0x00000027, 0x00000000, 0x00000000, 0, 0x00000002, 0x04000000, 0x00000002, 0x04000000 ],
|
|
775 => [ 0x00000007, 0x00000000, 0x00000000, 0, 0x00000080, 0x08000000, 0x00000080, 0x08000000 ],
|
|
737 => [ 0x00000080, 0x00000000, 0x00000000, 0, 0x00000008, 0x10000000, 0x00000008, 0x10010000 ],
|
|
720 => [ 0x00002000, 0x00000000, 0x00000000, 0, 0x00000040, 0x20000000, 0x00000040, 0x20080000 ],
|
|
850 => [ 0x00000003, 0x00000000, 0x00000000, 0, 0x00000001, 0x40000000, 0x0000019f, 0xdfd70000 ],
|
|
437 => [ 0x00000003, 0x00000000, 0x00000000, 0, 0x00000001, 0x80000000, 0x0000019f, 0xdfd70000 ],
|
|
65001 => [ 0x00000000, 0x00000000, 0x00000000, 0, 0x00000000, 0x00000000, 0x0000019f, 0xdfd70000 ] );
|
|
my $oemcp = locale_entry( $loc, "oemcp", 65001 );
|
|
my $maccp = locale_entry( $loc, "maccp", undef ) || $maccpmap{$oemcp} || 65001;
|
|
my $ebcdiccp = locale_entry( $loc, "ebcdiccp", undef ) || $ebcdiccpmap{$oemcp} || 500;
|
|
$ebcdiccp = 500 if (defined $loc->{oemcp} && $loc->{oemcp} == 65001) || (defined $loc->{maccp} && $loc->{maccp} == 65001);
|
|
my $ansicp = $ansicpmap{$oemcp} || $oemcp;
|
|
my @fontsig = (0) x 8;
|
|
my $sig = locale_entry( $loc, "fontsig", [] );
|
|
foreach my $i (0..7) { $fontsig[$i] |= $codepagemasks{$oemcp}->[$i]; }
|
|
foreach my $i (0..$#{$sig}) { $fontsig[$i] |= $sig->[$i]; }
|
|
$fontsig[3] |= 1 << 31;
|
|
$fontsig[3] |= 1 << 27 if $ireadinglayout == 1;
|
|
$fontsig[3] |= 1 << 28 if $ireadinglayout == 3;
|
|
|
|
# special cases for invariant locale
|
|
|
|
unless ($loc->{name})
|
|
{
|
|
$siso639langname = "iv";
|
|
$siso639langname2 = "ivl";
|
|
$senglanguage = $snativelangname = "Invariant Language";
|
|
$sengcountry = $snativectryname = "Invariant Country";
|
|
$sengdisplayname = "Invariant Language (Invariant Country)";
|
|
$snativedisplayname = "Invariant Language (Invariant Region)";
|
|
$sengcurrname = $snativecurrname = "International Monetary Fund";
|
|
$scurrency = "\x{00a4}";
|
|
$ifirstdayofweek = 0;
|
|
$igeoid = $geotable{"US"}->{id};
|
|
@stimeformat = ("HH:mm:ss");
|
|
@sshortdate = ("MM/dd/yyyy", "yyyy-MM-dd");
|
|
@slongdate = ("dddd, dd MMMM yyyy");
|
|
@syearmonth = ("yyyy MMMM");
|
|
@smonthday = ("MMMM dd", "MMMM d", "M/d", "MMM d");
|
|
@sshorttime = ("HH:mm", "hh:mm tt", "H:mm", "h:mm tt");
|
|
$srelativelongdate = "dddd, MMMM dd";
|
|
$sposinfinity = "Infinity";
|
|
$sneginfinity = "-Infinity";
|
|
$spositivesign = "+";
|
|
$ipospercent = $inegpercent = 0;
|
|
}
|
|
|
|
# output data
|
|
|
|
$locale_data .= pack "L<2",
|
|
add_string( $sname ), # name
|
|
add_string( $sopentypelang ); # LOCALE_SOPENTYPELANGUAGETAG
|
|
|
|
$locale_data .= pack "S<14",
|
|
$loc->{lcid} || 0x1000, # LOCALE_ILANGUAGE
|
|
$unique_lcid, # unique_lcid
|
|
locale_entry( $loc, "idigits", 2 ), # LOCALE_IDIGITS
|
|
locale_entry( $loc, "inegnumber", 1 ), # LOCALE_INEGNUMBER
|
|
$icurrdigits, # LOCALE_ICURRDIGITS
|
|
$icurrency, # LOCALE_ICURRENCY
|
|
$inegcurr, # LOCALE_INEGCURR
|
|
locale_entry( $loc, "ilzero", 1 ), # LOCALE_ILZERO
|
|
!$neutral, # LOCALE_INEUTRAL
|
|
$ifirstdayofweek, # LOCALE_IFIRSTDAYOFWEEK
|
|
$firstweekofyear, # LOCALE_IFIRSTWEEKOFYEAR
|
|
$geo->{dialcode} || 1 , # LOCALE_ICOUNTRY,
|
|
$measure, # LOCALE_IMEASURE
|
|
$digitsubstitution; # LOCALE_IDIGITSUBSTITUTION
|
|
|
|
$locale_data .= pack "L<18",
|
|
add_string( $sgrouping ), # LOCALE_SGROUPING
|
|
add_string( $smongrouping ), # LOCALE_SMONGROUPING
|
|
add_string( $slist ), # LOCALE_SLIST
|
|
add_string( $sdecimal ), # LOCALE_SDECIMAL
|
|
add_string( $sthousand ), # LOCALE_STHOUSAND
|
|
add_string( $scurrency ), # LOCALE_SCURRENCY
|
|
add_string( $smondecimalsep ), # LOCALE_SMONDECIMALSEP
|
|
add_string( $smonthousandsep ), # LOCALE_SMONTHOUSANDSEP
|
|
add_string( $spositivesign ), # LOCALE_SPOSITIVESIGN
|
|
add_string( $snegativesign ), # LOCALE_SNEGATIVESIGN
|
|
add_string( $s1159 ), # LOCALE_S1159
|
|
add_string( $s2359 ), # LOCALE_S2359
|
|
add_strarray( @snativedigits ), # LOCALE_SNATIVEDIGITS
|
|
add_strarray( @stimeformat ), # LOCALE_STIMEFORMAT
|
|
add_strarray( @sshortdate ), # LOCALE_SSHORTDATE
|
|
add_strarray( @slongdate ), # LOCALE_SLONGDATE
|
|
add_strarray( @syearmonth ), # LOCALE_SYEARMONTH
|
|
add_strarray( @sduration ); # LOCALE_SDURATION
|
|
|
|
$locale_data .= pack "S<8",
|
|
$idefaultlanguage || 0x1000, # LOCALE_IDEFAULTLANGUAGE
|
|
$ansicp, # LOCALE_IDEFAULTANSICODEPAGE
|
|
$oemcp, # LOCALE_IDEFAULTCODEPAGE
|
|
$maccp, # LOCALE_IDEFAULTMACCODEPAGE
|
|
$ebcdiccp, # LOCALE_IDEFAULTEBCDICCODEPAGE
|
|
$igeoid < 65536 ? $igeoid : 39070, # old_geoid
|
|
$papersize ? 1 : 9, # LOCALE_IPAPERSIZE
|
|
0; # FIXME # islamic_cal
|
|
|
|
$locale_data .= pack "L<24",
|
|
add_string( $icalendartype ), # LOCALE_ICALENDARTYPE
|
|
add_string( $sabbrevlangname ), # LOCALE_SABBREVLANGNAME
|
|
add_string( $siso639langname ), # LOCALE_SISO639LANGNAME
|
|
add_string( $senglanguage ), # LOCALE_SENGLANGUAGE
|
|
add_string( $snativelangname ), # LOCALE_SNATIVELANGNAME
|
|
add_string( $sengcountry ), # LOCALE_SENGCOUNTRY
|
|
add_string( $snativectryname ), # LOCALE_SNATIVECTRYNAME
|
|
add_string( $siso3166ctryname2 ), # LOCALE_SABBREVCTRYNAME
|
|
add_string( $territory ), # LOCALE_SISO3166CTRYNAME
|
|
add_string( $sintlsymbol ), # LOCALE_SINTLSYMBOL
|
|
add_string( $sengcurrname ), # LOCALE_SENGCURRNAME
|
|
add_string( $snativecurrname ), # LOCALE_SNATIVECURRNAME
|
|
add_fontsig( @fontsig ), # LOCALE_FONTSIGNATURE
|
|
add_string( $siso639langname2 ), # LOCALE_SISO639LANGNAME2
|
|
add_string( $siso3166ctryname2 ), # LOCALE_SISO3166CTRYNAME2
|
|
add_string( $sparent ), # LOCALE_SPARENT
|
|
add_strarray( @sdayname ), # LOCALE_SDAYNAME
|
|
add_strarray( @sabbrevdayname ), # LOCALE_SABBREVDAYNAME
|
|
add_strarray( @smonthname ), # LOCALE_SMONTHNAME
|
|
add_strarray( @sabbrevmonthname ), # LOCALE_SABBREVMONTHNAME
|
|
add_strarray( @sgenitivemonth ), # LOCALE_SGENITIVEMONTH
|
|
add_strarray( @sabbrevgenitivemonth ), # LOCALE_SABBREVGENITIVEMONTH
|
|
add_strarray( @scalnames ), # LOCALE_SCALNAMES
|
|
add_strarray( @{$loc->{sortnames}} ); # LOCALE_SSORTNAMES
|
|
|
|
$locale_data .= pack "S<6",
|
|
$inegpercent, # LOCALE_INEGATIVEPERCENT
|
|
$ipospercent, # LOCALE_IPOSITIVEPERCENT
|
|
0, # unknown
|
|
$ireadinglayout, # LOCALE_IREADINGLAYOUT
|
|
0x2a, # unknown
|
|
0x2a; # unknown
|
|
|
|
$locale_data .= pack "L<24",
|
|
0, # unknown
|
|
add_string( $sengdisplayname ), # LOCALE_SENGLISHDISPLAYNAME
|
|
add_string( $snativedisplayname ), # LOCALE_SNATIVEDISPLAYNAME
|
|
add_string( $spercent ), # LOCALE_SPERCENT
|
|
add_string( $snan ), # LOCALE_SNAN
|
|
add_string( $sposinfinity ), # LOCALE_SPOSINFINITY
|
|
add_string( $sneginfinity ), # LOCALE_SNEGINFINITY
|
|
0, # unknown
|
|
add_string( $serastring ), # CAL_SERASTRING
|
|
add_string( $serastring ), # CAL_SABBREVERASTRING
|
|
0, # unknown
|
|
add_string( $ssortlocale ), # LOCALE_SCONSOLEFALLBACKNAME
|
|
add_strarray( @sshorttime ), # LOCALE_SSHORTTIME
|
|
add_strarray( @sshortestdayname ), # CAL_SSHORTESTDAYNAME
|
|
0, # unknown
|
|
add_string( $ssortlocale ), # LOCALE_SSORTLOCALE
|
|
add_string( "0409:00000409" ), # FIXME # LOCALE_SKEYBOARDSTOINSTALL
|
|
add_string( $sscripts ), # LOCALE_SSCRIPTS
|
|
add_string( $srelativelongdate ), # LOCALE_SRELATIVELONGDATE
|
|
$igeoid, # LOCALE_IGEOID
|
|
add_string( $sshortestam || "a" ), # LOCALE_SSHORTESTAM
|
|
add_string( $sshortestpm || "p" ), # LOCALE_SSHORTESTPM
|
|
add_strarray( @smonthday ), # LOCALE_SMONTHDAY
|
|
add_string( "k0-windows-us" ) # FIXME # keyboard_layout
|
|
}
|
|
|
|
# output language groups
|
|
|
|
my %groups;
|
|
add_registry_key( $nlskey, "Locale", "00000409" );
|
|
foreach my $loc (@locales)
|
|
{
|
|
next unless defined $loc->{lcid};
|
|
next if ($loc->{lcid} & 0x80000000);
|
|
next if !defined($loc->{alias}) && $loc->{name} !~ /-$loc->{territory}/; # skip neutral locales
|
|
my $group = locale_entry( $loc, "group", 1 );
|
|
my $name = sprintf( "%08x", $loc->{lcid} );
|
|
my $val = sprintf( "%x", $group );
|
|
add_registry_string_value( $nlskey, "Locale", $name, $val ) unless ($loc->{lcid} & 0x000f0000);
|
|
add_registry_string_value( $nlskey, "Locale\\Alternate Sorts", $name, $val ) if $loc->{name} =~ /_/;
|
|
$groups{$val} = 1;
|
|
}
|
|
foreach my $group (keys %groups) { add_registry_string_value( $nlskey, "Language Groups", $group, "1" ); }
|
|
|
|
# output calendar data
|
|
|
|
my $calendar_data = "";
|
|
foreach my $cal (@calendars)
|
|
{
|
|
my $scalname = $cal->{name};
|
|
my $iyearoffsetrange = 0;
|
|
my $itwodigityearmax = $cal->{itwodigityearmax};
|
|
my @sshortdate;
|
|
my @syearmonth;
|
|
my @slongdate;
|
|
my @serastring;
|
|
my @sdayname;
|
|
my @sabbrevdayname;
|
|
my @smonthname;
|
|
my @sabbrevmonthname;
|
|
my @smonthday;
|
|
my @sabbreverastring;
|
|
my @sshortestdayname;
|
|
|
|
my $type = $cal->{type};
|
|
if (defined $cal->{locale} && defined $type)
|
|
{
|
|
my $loc = $lcnames{$cal->{locale}};
|
|
my $fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yMd' and not(\@alt)]" );
|
|
push @sshortdate, $fmt if $fmt;
|
|
$fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yyyyMd' and not(\@alt)]" );
|
|
push @sshortdate, $fmt if $fmt;
|
|
$fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yMMMd' and not(\@alt)]" );
|
|
push @sshortdate, $fmt if $fmt;
|
|
$fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateTimeFormats/availableFormats/dateFormatItem[\@id='yyyyMMMd' and not(\@alt)]" );
|
|
push @sshortdate, $fmt if $fmt;
|
|
@sshortdate = map convert_date_format($_), @sshortdate;
|
|
$fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateFormats/dateFormatLength[\@type='full']/dateFormat/pattern[not(\@alt)]" );
|
|
push @slongdate, $fmt if $fmt;
|
|
$fmt = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/dateFormats/dateFormatLength[\@type='long']/dateFormat/pattern[not(\@alt)]" );
|
|
push @slongdate, $fmt if $fmt;
|
|
@slongdate = map convert_date_format($_), @slongdate;
|
|
|
|
foreach my $n (1..13)
|
|
{
|
|
my $name = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/months/monthContext[\@type='format']/monthWidth[\@type='wide']/month[\@type='$n' and not(\@yeartype)]" );
|
|
my $abbrev = loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/months/monthContext[\@type='format']/monthWidth[\@type='abbreviated']/month[\@type='$n' and not(\@yeartype)]" );
|
|
push @smonthname, $name || "";
|
|
push @sabbrevmonthname, $abbrev || $name || "";
|
|
}
|
|
|
|
$scalname ||= loc_query( $loc, "/ldml/localeDisplayNames/types/type[\@key='calendar' and \@type='$type']" );
|
|
if (defined $cal->{eras})
|
|
{
|
|
my @eras;
|
|
my $idx = 1;
|
|
foreach my $era (@{$cal->{eras}})
|
|
{
|
|
my $start = xml_query( $suppl, "/supplementalData/calendarData/calendar[\@type='$type']/eras/era[\@type='$era']/\@start" );
|
|
next unless $start =~ /^(-?\d+)-(\d+)-(\d+)/;
|
|
my ($year, $mon, $day, $zero, $first) = ($1, $2, $3, $1 - 1, 1);
|
|
if ($zero < 0)
|
|
{
|
|
$first -= $zero;
|
|
$year = 1;
|
|
$itwodigityearmax = 2049 - $zero;
|
|
}
|
|
unshift @eras, pack( "S<8", 6, $idx++, $year, $mon, $day, $zero, $first, 0 );
|
|
push @serastring, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/eras/eraAbbr/era[\@type='$era']" );
|
|
push @sabbreverastring, loc_query( $loc, "/ldml/dates/calendars/calendar[\@type='$type']/eras/eraNarrow/era[\@type='$era']" );
|
|
}
|
|
$iyearoffsetrange = add_str_data( pack "S<L<*", scalar @eras, map { add_str_data($_); } @eras );
|
|
}
|
|
}
|
|
|
|
@sshortdate = @{$cal->{sshortdate}} if defined $cal->{sshortdate} && !@sshortdate;
|
|
@syearmonth = @{$cal->{syearmonth}} if defined $cal->{syearmonth};
|
|
@slongdate = @{$cal->{slongdate}} if defined $cal->{slongdate} && !@slongdate;
|
|
@serastring = @{$cal->{serastring}} if defined $cal->{serastring} && !@serastring;
|
|
@sdayname = @{$cal->{sdayname}} if defined $cal->{sdayname};
|
|
@sabbrevdayname = @{$cal->{sabbrevdayname}} if defined $cal->{sabbrevdayname};
|
|
@smonthname = @{$cal->{smonthname}} if defined $cal->{smonthname} && !join("",@smonthname);
|
|
@sabbrevmonthname = @{$cal->{sabbrevmonthname}} if defined $cal->{sabbrevmonthname} && !join("",@sabbrevmonthname);
|
|
@smonthday = @{$cal->{smonthday}} if defined $cal->{smonthday};
|
|
@sabbreverastring = @{$cal->{sabbreverastring}} if defined $cal->{sabbreverastring} && !@sabbreverastring;
|
|
@sshortestdayname = @{$cal->{sshortestdayname}} if defined $cal->{sshortestdayname};
|
|
my $srelativelongdate = $cal->{srelativelongdate};
|
|
|
|
@serastring = ("A.D.") unless @serastring;
|
|
@sabbreverastring = ("AD") unless @sabbreverastring;
|
|
|
|
if ($cal->{id} != 1) # calendar 1 is a placeholder, information is fetched from locale instead
|
|
{
|
|
@sshortdate = ("") unless @sshortdate;
|
|
@syearmonth = ("") unless @syearmonth;
|
|
@slongdate = ("") unless @slongdate;
|
|
@sdayname = ("") x 7 unless @sdayname;
|
|
@sabbrevdayname = ("") x 7 unless @sabbrevdayname;
|
|
@sshortestdayname = ("") x 7 unless @sshortestdayname;
|
|
@smonthname = ("") x 13 unless @smonthname;
|
|
@sabbrevmonthname = ("") x 13 unless @sabbrevmonthname;
|
|
@smonthday = ("") unless @smonthday;
|
|
}
|
|
|
|
$calendar_data .= pack "S<2L<17",
|
|
$cal->{id}, # CAL_ICALINTVALUE
|
|
$itwodigityearmax || 99, # CAL_ITWODIGITYEARMAX
|
|
add_strarray( @sshortdate ), # CAL_SSHORTDATE
|
|
add_strarray( @syearmonth ), # CAL_SYEARMONTH
|
|
add_strarray( @slongdate ), # CAL_SLONGDATE
|
|
add_strarray( @serastring ), # CAL_SERASTRING
|
|
$iyearoffsetrange, # CAL_IYEAROFFSETRANGE
|
|
add_strarray( @sdayname ), # CAL_SDAYNAME
|
|
add_strarray( @sabbrevdayname ), # CAL_SABBREVDAYNAME
|
|
add_strarray( @smonthname ), # CAL_SMONTHNAME
|
|
add_strarray( @sabbrevmonthname ), # CAL_SABBREVMONTHNAME
|
|
add_string( $scalname ), # CAL_SCALNAME
|
|
add_strarray( @smonthday ), # CAL_SMONTHDAY
|
|
add_strarray( @sabbreverastring ), # CAL_SABBREVERASTRING
|
|
add_strarray( @sshortestdayname ), # CAL_SSHORTESTDAYNAME
|
|
add_string( $srelativelongdate ); # CAL_SRELATIVELONGDATE
|
|
}
|
|
|
|
# output locale header
|
|
|
|
my $nb_lcids = scalar keys %lcids;
|
|
my $nb_locales = scalar grep { !defined $_->{alias} } @locales;
|
|
my $nb_lcnames = scalar keys %lcnames;
|
|
my $locale_size = length($locale_data) / $nb_locales;
|
|
my $nb_calendars = scalar @calendars;
|
|
my $calendar_size = length($calendar_data) / $nb_calendars;
|
|
my $lcids_offset = 19 * 4; # size of header
|
|
my $lcnames_offset = $lcids_offset + length $lcid_data;
|
|
my $locales_offset = $lcnames_offset + length $lcname_data;
|
|
my $calendar_offset = $locales_offset + length $locale_data;
|
|
my $strings_offset = $calendar_offset + length $calendar_data;
|
|
|
|
my $locale_header = pack "L<7S<4L<S<2L<3S<2L<4",
|
|
8, # offset
|
|
0,
|
|
7, # version
|
|
0x5344534e, # magic
|
|
0, 0, 0,
|
|
0,
|
|
$nb_lcids,
|
|
$nb_locales,
|
|
$locale_size,
|
|
$locales_offset,
|
|
$nb_lcnames,
|
|
0,
|
|
$lcids_offset,
|
|
$lcnames_offset,
|
|
0,
|
|
$nb_calendars,
|
|
$calendar_size,
|
|
$calendar_offset,
|
|
$strings_offset,
|
|
0, 0;
|
|
|
|
return align_string( 4, $locale_header . $lcid_data . $lcname_data . $locale_data . $calendar_data . $string_data );
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build the charmaps table for locale.nls
|
|
sub build_charmaps_data()
|
|
{
|
|
my $data = "";
|
|
|
|
# MAP_FOLDDIGITS
|
|
my @digits = (ord('0') .. ord('9'));
|
|
$digitmap_table[0x3007] = $digits[0]; # Ideographic Zero
|
|
@digitmap_table[0x0c78..0x0c7b] = @digits[0..3]; # Telugu Fraction Digits
|
|
@digitmap_table[0x0c7c..0x0c7e] = @digits[1..3]; # Telugu Fraction Digits
|
|
@digitmap_table[0x3021..0x3029] = @digits[1..9]; # Hangzhou Numerals
|
|
@digitmap_table[0xa8e0..0xa8e9] = @digits; # Combining Devanagari Digits
|
|
@digitmap_table[0x10107..0x1010f] = @digits[1..9]; # Aegean Numbers
|
|
$digitmap_table[0x10320] = $digits[1]; # Old Italic Numerals
|
|
$digitmap_table[0x10321] = $digits[5]; # Old Italic Numerals
|
|
$data .= dump_binary_case_table( @digitmap_table );
|
|
|
|
# CJK compatibility map
|
|
$data .= dump_binary_case_table( @cjk_compat_table );
|
|
|
|
# LCMAP_HIRAGANA/KATAKANA
|
|
my (@hiragana_table, @katakana_table);
|
|
foreach my $ch (0x3041..0x3096, 0x309d..0x309e)
|
|
{
|
|
$hiragana_table[$ch + 0x60] = $ch;
|
|
$katakana_table[$ch] = $ch + 0x60;
|
|
}
|
|
$data .= dump_binary_case_table( @hiragana_table ) . dump_binary_case_table( @katakana_table );
|
|
|
|
# LCMAP_HALFWIDTH/FULLWIDTH
|
|
$halfwidth_table[0x2018] = 0x0027;
|
|
$halfwidth_table[0x2019] = 0x0027;
|
|
$halfwidth_table[0x201c] = 0x0022;
|
|
$halfwidth_table[0x201d] = 0x0022;
|
|
$halfwidth_table[0x309b] = 0xff9e;
|
|
$halfwidth_table[0x309c] = 0xff9f;
|
|
$fullwidth_table[0x309b] = 0x3099;
|
|
$fullwidth_table[0x309c] = 0x309a;
|
|
$data .= dump_binary_case_table( @halfwidth_table ) . dump_binary_case_table( @fullwidth_table );
|
|
|
|
# LCMAP_TRADITIONAL/SIMPLIFIED_CHINESE
|
|
$data .= dump_binary_case_table( @chinese_traditional_table ) . dump_binary_case_table( @chinese_simplified_table );
|
|
|
|
# FIXME: some more unknown tables here
|
|
|
|
return $data;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build the geoids table for locale.nls
|
|
sub build_geoids_data()
|
|
{
|
|
my $data = "";
|
|
my %index;
|
|
my $idx = 0;
|
|
my @geo_header = (0x00650067, 0x0000006f, 0, 4 * 7, scalar @geoids, 0, 0);
|
|
|
|
foreach my $geo (@geoids)
|
|
{
|
|
my $id = $geo->{id};
|
|
$geo = $geo->{alias} if defined $geo->{alias};
|
|
my $lat = "0.000";
|
|
my $long = "0.000";
|
|
my $iso2 = $geo->{iso2} || "XX";
|
|
my $iso3 = $geo->{iso3} || "XX";
|
|
my $isregion = $geo->{region} || (defined $geo->{uncode} && !defined $geo->{iso2});
|
|
my $sintlsymbol = $geo->{sintlsymbol} || "XDR";
|
|
my $scurrency = $geo->{scurrency} || "\x{00a4}";
|
|
|
|
$data .= pack( "L<", $id );
|
|
$data .= pad_string( 24, encode( "UTF16LE", $lat ));
|
|
$data .= pad_string( 24, encode( "UTF16LE", $long ));
|
|
$data .= pack( "L<2", $isregion ? 14 : 16, $geo->{parentid} || 39070 );
|
|
$data .= pad_string( 8, encode( "UTF16LE", $iso2 ));
|
|
$data .= pad_string( 8, encode( "UTF16LE", $iso3 ));
|
|
$data .= pack( "S<2", $geo->{uncode} || 0, $geo->{dialcode} || 0 );
|
|
$data .= pad_string( 8, encode( "UTF16LE", $sintlsymbol ));
|
|
$data .= pad_string( 16, encode( "UTF16LE", $scurrency ));
|
|
$index{$geo->{name}} = $idx if $geo->{name};
|
|
$idx++;
|
|
}
|
|
$index{"XX"} = $index{"001"};
|
|
|
|
$geo_header[5] = $geo_header[3] + length $data;
|
|
$geo_header[6] = scalar keys %index;
|
|
|
|
foreach my $name (sort keys %index)
|
|
{
|
|
$data .= pad_string( 8, encode( "UTF16LE", $name ));
|
|
$data .= pack "L<", $index{$name};
|
|
}
|
|
|
|
$geo_header[2] = $geo_header[3] + length $data;
|
|
return pack( "L<7", @geo_header ) . $data;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build a binary locale table
|
|
sub dump_locales($$)
|
|
{
|
|
my ($filename, $chartypes) = @_;
|
|
|
|
printf "Building $filename\n";
|
|
|
|
my $locale_data = build_locale_data();
|
|
my $charmaps_data = build_charmaps_data();
|
|
my $geoids_data = build_geoids_data();
|
|
my $scripts_data = ""; # FIXME
|
|
|
|
my @header = ( 0 ) x 8;
|
|
$header[0] = 4 * scalar @header; # chartypes offset
|
|
$header[4] = $header[0] + length $chartypes; # locales offset
|
|
$header[5] = $header[4] + length $locale_data; # charmaps offset
|
|
$header[6] = $header[5] + length $charmaps_data; # geoids offset
|
|
$header[7] = $header[6] + length $geoids_data; # scripts offset
|
|
|
|
open OUTPUT, ">$filename.new" or die "Cannot create $filename";
|
|
print OUTPUT pack "L<*", @header;
|
|
print OUTPUT $chartypes, $locale_data, $charmaps_data, $geoids_data, $scripts_data;
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# return the day of week of the first of the month
|
|
sub month_first_dow($$)
|
|
{
|
|
my ($year, $month) = @_;
|
|
my @time = gmtime( timegm_modern( 0, 0, 0, 1, $month - 1, $year ));
|
|
return $time[6];
|
|
}
|
|
|
|
|
|
################################################################
|
|
# compare system time values
|
|
sub compare_systime($$)
|
|
{
|
|
my ($a, $b) = @_;
|
|
return $a->[0] <=> $b->[0] ||
|
|
$a->[1] <=> $b->[1] ||
|
|
$a->[2] <=> $b->[2] ||
|
|
$a->[3] <=> $b->[3] ||
|
|
$a->[4] <=> $b->[4] ||
|
|
$a->[5] <=> $b->[5] ||
|
|
$a->[6] <=> $b->[6];
|
|
}
|
|
|
|
|
|
################################################################
|
|
# compare the zone transition date with the rule date
|
|
sub compare_transition_date($$$$)
|
|
{
|
|
my ($stdoff, $isdst, $zone, $rule) = @_;
|
|
|
|
if (scalar @{$zone} <= 1)
|
|
{
|
|
return (!defined($zone->[0]) || $zone->[0] > $rule->[0]) ? 1 : -1;
|
|
}
|
|
|
|
my @date = parse_transition_date( $stdoff, $isdst, $zone->[0], $zone->[1], $zone->[2], $zone->[3] || 0 );
|
|
return compare_systime( \@date, $rule );
|
|
}
|
|
|
|
|
|
################################################################
|
|
# get the Windows zone names from the CLDR data
|
|
sub load_windows_zones()
|
|
{
|
|
my $current_name;
|
|
my %names;
|
|
my $base = "cldr-release-$CLDRVERSION";
|
|
my $INPUT = open_data_file( "cldr", "$base/common/supplemental/windowsZones.xml" );
|
|
while (<$INPUT>)
|
|
{
|
|
if (/<!-- +(\(UTC.*) -->.*/)
|
|
{
|
|
$current_name = $1;
|
|
}
|
|
if (/<mapZone other="(.*)" territory="001" type="(.*)"\/>/)
|
|
{
|
|
$names{$1} = [ $current_name, $2 ];
|
|
}
|
|
}
|
|
close $INPUT;
|
|
return %names;
|
|
}
|
|
|
|
|
|
################################################################
|
|
# parse a transition date specification from the tzdata files
|
|
sub parse_transition_date($$@)
|
|
{
|
|
use integer;
|
|
my ($stdoff, $isdst, $year, $in, $on, $at) = @_;
|
|
|
|
$on = "1" unless defined $on;
|
|
$at = "0" unless defined $at;
|
|
|
|
my %months = ( Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6,
|
|
Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12 );
|
|
my %days = ( Sun => 0, Mon => 1, Tue => 2, Wed => 3, Thu => 4, Fri => 5, Sat => 6 );
|
|
|
|
my $mon = $in ? $months{$in} : 1;
|
|
my ($week, $dow, $flag, $time, $sec);
|
|
my $first = month_first_dow( $year, $mon );
|
|
|
|
if ($on =~ /^last(.*)$/)
|
|
{
|
|
$week = 5;
|
|
$dow = $days{$1};
|
|
}
|
|
elsif ($on =~ /^(.*)>=(\d+)$/)
|
|
{
|
|
$dow = $days{$1};
|
|
my $diff = ($first + 6 - $dow) % 7;
|
|
$week = $2 >= 25 ? 5 : ($2 + 6 + $diff) / 7;
|
|
}
|
|
elsif ($on =~ /^(.*)<=(\d+)$/)
|
|
{
|
|
$dow = $days{$1};
|
|
my $diff = ($first + $2 + 6 - $dow) % 7;
|
|
$week = ($2 + 6 - $diff) / 7;
|
|
if (!$week)
|
|
{
|
|
$week = 5;
|
|
if (!--$mon) { $mon = 12; $year--; }
|
|
}
|
|
}
|
|
elsif ($on =~ /^\d+$/)
|
|
{
|
|
$dow = ($first + $on - 1) % 7;
|
|
$week = $on >= 25 ? 5 : ($on + 6) / 7;
|
|
}
|
|
else
|
|
{
|
|
die "unsupported date specification $year $in $on $at";
|
|
}
|
|
|
|
if ($at =~ /^(\d+):(\d+):(\d+)([uws]?)$/)
|
|
{
|
|
$time = $1 * 60 + $2;
|
|
$sec = $3;
|
|
$flag = $4;
|
|
}
|
|
elsif ($at =~ /^(\d+):(\d+)([uws]?)$/)
|
|
{
|
|
$time = $1 * 60 + $2;
|
|
$flag = $3;
|
|
}
|
|
elsif ($at =~ /^(\d+)([uws]?)$/)
|
|
{
|
|
$time = $1 * 60;
|
|
$flag = $2;
|
|
}
|
|
else
|
|
{
|
|
die "unsupported time specification $year $in $on $at";
|
|
}
|
|
|
|
$flag ||= "w";
|
|
$time -= $stdoff if $flag eq "u";
|
|
$time += 60 if !$isdst && $flag ne "w";
|
|
|
|
if ($time < 0) # previous day
|
|
{
|
|
$week-- if $week < 5 && $dow == month_first_dow( $year, $mon );
|
|
$week-- if $week == 5 && $dow == month_first_dow( $year + ($mon == 12), $mon % 12 + 1 );
|
|
if (!$week)
|
|
{
|
|
$week = 5;
|
|
if (!--$mon) { $mon = 12; $year--; }
|
|
}
|
|
$dow = ($dow + 6) % 7;
|
|
$time += 24 * 60;
|
|
}
|
|
|
|
return ($year, $mon, $week, $dow, $time / 60, $time % 60, $sec || 0);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# parse a system time value as a SYSTEMTIME structure
|
|
sub pack_systime(@)
|
|
{
|
|
my ($year, $mon, $week, $dow, $hour, $min, $sec) = @_;
|
|
return pack "S<8", 0, $mon, $dow, $week, $hour < 24 ? ($hour, $min, $sec, 0) : (23, 59, 59, 999);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# parse a timezone offset from the tzdata files
|
|
sub parse_tz_offset($)
|
|
{
|
|
my ($hour, $min) = split /:/, shift;
|
|
$min ||= 0;
|
|
return $hour < 0 ? -$hour * 60 + $min : -$hour * 60 - $min; # invert sign
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build the timezone data
|
|
sub dump_timezones($@)
|
|
{
|
|
my $filename = shift;
|
|
my $FIRST_YEAR = 2000;
|
|
my $LAST_YEAR = 2030;
|
|
|
|
my %names = load_windows_zones();
|
|
my %zones;
|
|
my %rules;
|
|
my %links;
|
|
my %res_indices;
|
|
|
|
printf "Building $filename\n";
|
|
|
|
open OUTPUT, ">$filename.new" or die "Cannot create $filename";
|
|
print OUTPUT "/* Automatically generated; DO NOT EDIT!! */\n\n";
|
|
print OUTPUT "#include \"winresrc.h\"\n\n";
|
|
print OUTPUT "#pragma makedep po\n\n";
|
|
print OUTPUT "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n\n";
|
|
print OUTPUT "STRINGTABLE\n{\n";
|
|
|
|
# load tzdata files
|
|
|
|
foreach my $filename (@_)
|
|
{
|
|
my $FILE = open_data_file( "tzdata", $filename );
|
|
my $zonename;
|
|
while (<$FILE>)
|
|
{
|
|
chomp;
|
|
s/\#.*$//;
|
|
next if /^\s*$/;
|
|
my @fields = split /\s+/;
|
|
if ($fields[0] eq "Zone" || ($zonename && $fields[0] eq ""))
|
|
{
|
|
shift @fields;
|
|
$zonename = shift @fields unless $zonename;
|
|
my ($stdoff, $rules, $dummy, @date) = @fields;
|
|
$zones{$zonename} ||= [ ];
|
|
push @{$zones{$zonename}}, [ parse_tz_offset( $stdoff ), $rules, @date ];
|
|
$zonename = undef unless @date; # last entry doesn't have an until date
|
|
next;
|
|
}
|
|
if ($fields[0] eq "Rule")
|
|
{
|
|
shift @fields;
|
|
my ($rulename, $from, $to, $dummy, $in, $on, $at, $save) = @fields;
|
|
$to = $from if $to eq "only";
|
|
$to = $LAST_YEAR if $to eq "max";
|
|
push @{$rules{$rulename}}, [ parse_tz_offset( $save ), $from, $to, $in, $on, $at ];
|
|
next;
|
|
}
|
|
if ($fields[0] eq "Link")
|
|
{
|
|
$links{$fields[2]} = $fields[1];
|
|
next;
|
|
}
|
|
die "unrecognized line $_";
|
|
}
|
|
close $FILE;
|
|
}
|
|
|
|
foreach my $name (sort { uc($a) cmp uc($b) } keys %names)
|
|
{
|
|
my ($display, $zone) = @{$names{$name}};
|
|
$zone = $links{$zone} if defined $links{$zone};
|
|
|
|
# build list of transitions
|
|
|
|
my @transitions;
|
|
my @from_date = ( 1 );
|
|
my $last_stdoff = 0;
|
|
for (my $i = 0; $i < scalar @{$zones{$zone}}; $i++)
|
|
{
|
|
my ($stdoff, $rule, @until_date) = @{$zones{$zone}->[$i]};
|
|
my $isdst = ($last_stdoff != $stdoff);
|
|
$from_date[0] ||= $LAST_YEAR;
|
|
my @systime = parse_transition_date( $stdoff, $isdst, @from_date );
|
|
push @transitions, [ $stdoff, -1, \@systime ];
|
|
|
|
if (defined $rules{$rule})
|
|
{
|
|
foreach my $r (@{$rules{$rule}})
|
|
{
|
|
my ($offset, $from, $to, $in, $on, $at) = @{$r};
|
|
foreach my $year ($from..$to)
|
|
{
|
|
next if $year < $from_date[0];
|
|
next if $until_date[0] && $year > $until_date[0];
|
|
my @systime = parse_transition_date( $stdoff, !!$offset, $year, $in, $on, $at );
|
|
next if compare_transition_date( $stdoff, $isdst, \@until_date, \@systime ) <= 0;
|
|
my $ret = compare_transition_date( $stdoff, $isdst, \@from_date, \@systime );
|
|
next if $ret > 0;
|
|
pop @transitions if !$ret; # remove transition if there's a dst change at the same time
|
|
push @transitions, [ $stdoff, $offset, \@systime ];
|
|
}
|
|
}
|
|
}
|
|
@from_date = @until_date;
|
|
$last_stdoff = $stdoff;
|
|
}
|
|
@transitions = sort { compare_systime( $a->[2], $b->[2] ) } @transitions;
|
|
|
|
# build per-year dynamic info
|
|
|
|
my @info;
|
|
my $last_dstoff = 0;
|
|
my $last_dst = 0;
|
|
my $year = $FIRST_YEAR;
|
|
while ($year <= $LAST_YEAR)
|
|
{
|
|
if (@transitions && $transitions[0]->[2]->[0] < $year)
|
|
{
|
|
$last_stdoff = $transitions[0]->[0];
|
|
shift @transitions;
|
|
next;
|
|
}
|
|
my ($std, $dst, @trans);
|
|
my $cur_stdoff = $last_stdoff;
|
|
my $cur_dstoff = ($name =~ /^UTC/) ? 0 : -60;
|
|
while (@transitions && $transitions[0]->[2]->[0] == $year)
|
|
{
|
|
my $t = shift @transitions;
|
|
my ($stdoff, $dstoff, $systime) = @{$t};
|
|
$systime = pack_systime( @{$systime} );
|
|
if (!$dstoff) # std
|
|
{
|
|
$cur_stdoff = $stdoff unless $std;
|
|
$std = $systime;
|
|
}
|
|
elsif ($dstoff != -1) # dst
|
|
{
|
|
$cur_dstoff = $dstoff unless $dst;
|
|
$dst ||= $systime;
|
|
}
|
|
elsif ($stdoff != $last_stdoff) # rule transition
|
|
{
|
|
# Handle a special case: Samoa moved to the other side of
|
|
# the date line between 2011-12-03 and 2012-01-01,
|
|
# entirely skipping the day 2011-12-31. We ignore this
|
|
# change because it happens on a year boundary and more
|
|
# importantly it would generate on offset of -25 hours,
|
|
# which some programs (e.g., Mono) do not like. See
|
|
# https://bugs.winehq.org/show_bug.cgi?id=51758
|
|
|
|
if ($last_stdoff - $stdoff < 24 * 60)
|
|
{
|
|
@trans = ($last_stdoff, $stdoff, $systime);
|
|
$cur_stdoff = $stdoff;
|
|
}
|
|
}
|
|
elsif ($dst) # rule transition with no stdoff change
|
|
{
|
|
$std = $systime;
|
|
}
|
|
$last_dstoff = ($dstoff == -1) ? 0 : $dstoff;
|
|
}
|
|
$last_stdoff = $cur_stdoff;
|
|
|
|
if ($cur_dstoff > 0) # swap std and dst to ensure that offset is negative
|
|
{
|
|
($std, $dst) = ($dst, $std);
|
|
$cur_stdoff += $cur_dstoff;
|
|
$cur_dstoff = -$cur_dstoff;
|
|
}
|
|
|
|
if (@trans)
|
|
{
|
|
# heuristic to prefer switching dst
|
|
if ($last_dst == $year - 1 || (!$last_dst && $trans[0] > $trans[1]))
|
|
{
|
|
$dst ||= $trans[2];
|
|
$cur_stdoff = $trans[0];
|
|
$cur_dstoff = $trans[1] - $trans[0];
|
|
}
|
|
else
|
|
{
|
|
$std ||= $trans[2];
|
|
$cur_stdoff = $trans[1];
|
|
$cur_dstoff = $trans[0] - $trans[1];
|
|
}
|
|
}
|
|
|
|
if ($std || $dst)
|
|
{
|
|
$std ||= pack_systime( parse_transition_date( 0, 0, $year, "Jan", 1 ));
|
|
$dst ||= pack_systime( parse_transition_date( 0, 0, $year, "Jan", 1 ));
|
|
$last_dst = $year;
|
|
}
|
|
else
|
|
{
|
|
$std = pack "S<8", 0;
|
|
$dst = pack "S<8", 0;
|
|
$cur_stdoff += $last_dstoff;
|
|
}
|
|
$info[$year++] = pack( "l<3", $cur_stdoff, 0, $cur_dstoff ) . $std . $dst;
|
|
}
|
|
|
|
# output registry keys
|
|
|
|
my $std_name = $name eq "UTC" ? "Coordinated Universal Time" : $name;
|
|
my $dlt_name = $std_name =~ s/Standard Time/Daylight Time/r;
|
|
my $res_idx = hex( substr( Digest::SHA::sha1_hex($name), -3, 3 )) << 4;
|
|
$res_idx += 16 while exists $res_indices{$res_idx};
|
|
$res_indices{$res_idx} = 1;
|
|
|
|
add_registry_string_value( $zonekey, $name, "Display", $display );
|
|
add_registry_string_value( $zonekey, $name, "Std", $std_name );
|
|
add_registry_string_value( $zonekey, $name, "Dlt", $dlt_name );
|
|
add_registry_string_value( $zonekey, $name, "MUI_Std", sprintf( "\@tzres.dll,-%u", $res_idx ));
|
|
add_registry_string_value( $zonekey, $name, "MUI_Dlt", sprintf( "\@tzres.dll,-%u", $res_idx + 1 ));
|
|
add_registry_string_value( $zonekey, $name, "MUI_Display", sprintf( "\@tzres.dll,-%u", $res_idx + 2 ));
|
|
add_registry_binary_value( $zonekey, $name, "TZI", $info[$LAST_YEAR] );
|
|
|
|
printf OUTPUT "%7d \"#msgctxt#maximum 31 characters#%s\"\n", $res_idx, $std_name;
|
|
printf OUTPUT "%7d \"#msgctxt#maximum 31 characters#%s\"\n", $res_idx + 1, $dlt_name;
|
|
printf OUTPUT "%7d \"%s\"\n", $res_idx + 2, $display;
|
|
|
|
my $first_year = $FIRST_YEAR;
|
|
my $last_year = $LAST_YEAR;
|
|
$last_year-- while $last_year > $FIRST_YEAR && $info[$last_year] eq $info[$last_year - 1];
|
|
$first_year++ while $first_year < $last_year && $info[$first_year] eq $info[$last_year];
|
|
|
|
next if $last_year <= $first_year;
|
|
|
|
foreach my $i ($first_year..$last_year)
|
|
{
|
|
add_registry_binary_value( $zonekey, "$name\\Dynamic DST", $i, $info[$i] );
|
|
}
|
|
add_registry_dword_value( $zonekey, "$name\\Dynamic DST", "FirstEntry", $first_year );
|
|
add_registry_dword_value( $zonekey, "$name\\Dynamic DST", "LastEntry", $last_year );
|
|
}
|
|
|
|
print OUTPUT "}\n";
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# build the script to create registry keys
|
|
sub dump_registry_script($%)
|
|
{
|
|
my ($filename, %keys) = @_;
|
|
my $indent = 1;
|
|
my @prev;
|
|
|
|
printf "Building %s\n", $filename;
|
|
open OUTPUT, ">$filename.new" or die "Cannot create $filename";
|
|
print OUTPUT "HKLM\n{\n";
|
|
foreach my $k (sort { ($a =~ tr/a-z\\/A-Z\001/r) cmp ($b =~ tr/a-z\\/A-Z\001/r) } keys %keys)
|
|
{
|
|
my @subkeys = split /\\/, $k;
|
|
while (@prev && @subkeys && $prev[0] eq $subkeys[0]) { shift @prev; shift @subkeys; }
|
|
while (@prev) { printf OUTPUT "%*s}\n", 4 * --$indent, ""; shift @prev; }
|
|
my ($def, @vals) = @{$keys{$k}};
|
|
for (my $i = 0; $i < @subkeys; $i++)
|
|
{
|
|
my $name = $subkeys[$i];
|
|
my $prefix = "";
|
|
if ($name =~ /^-/)
|
|
{
|
|
$name =~ s/^-//;
|
|
$prefix = "NoRemove ";
|
|
}
|
|
if ($name =~ /\s/)
|
|
{
|
|
$name = "'$name'";
|
|
}
|
|
printf OUTPUT "%*s%s%s%s\n%*s{\n", 4 * $indent, "", $prefix, $name,
|
|
$i == $#subkeys && $def ? " = s '$def'" : "", 4 * $indent, "";
|
|
$indent++;
|
|
}
|
|
foreach my $v (sort @vals) { printf OUTPUT "%*sval $v\n", 4 * $indent, ""; }
|
|
@prev = split /\\/, $k;
|
|
}
|
|
while (@prev) { printf OUTPUT "%*s}\n", 4 * --$indent, ""; shift @prev; }
|
|
printf OUTPUT "}\n";
|
|
close OUTPUT;
|
|
save_file($filename);
|
|
}
|
|
|
|
|
|
################################################################
|
|
# save a file if modified
|
|
sub save_file($)
|
|
{
|
|
my $file = shift;
|
|
if (-f $file && !system "cmp $file $file.new >/dev/null")
|
|
{
|
|
unlink "$file.new";
|
|
}
|
|
else
|
|
{
|
|
rename "$file.new", "$file";
|
|
}
|
|
}
|
|
|
|
|
|
################################################################
|
|
# main routine
|
|
|
|
chdir ".." if -f "./make_unicode";
|
|
load_data();
|
|
dump_bidi_dir_table( "dlls/gdi32/uniscribe/direction.c" );
|
|
dump_bidi_dir_table( "dlls/dwrite/direction.c" );
|
|
dump_bidi_dir_table( "dlls/wineps.drv/direction.c" );
|
|
dump_mirroring( "dlls/gdi32/uniscribe/mirror.c" );
|
|
dump_mirroring( "dlls/dwrite/mirror.c" );
|
|
dump_bracket( "dlls/gdi32/uniscribe/bracket.c" );
|
|
dump_bracket( "dlls/dwrite/bracket.c" );
|
|
dump_shaping( "dlls/gdi32/uniscribe/shaping.c" );
|
|
dump_arabic_shaping( "dlls/dwrite/shapers/arabic_table.c" );
|
|
dump_linebreak( "dlls/gdi32/uniscribe/linebreak.c" );
|
|
dump_linebreak( "dlls/dwrite/linebreak.c" );
|
|
dump_scripts( "dlls/dwrite/scripts" );
|
|
dump_indic( "dlls/gdi32/uniscribe/indicsyllable.c" );
|
|
dump_vertical( "dlls/win32u/vertical.c", 1 );
|
|
dump_vertical( "dlls/wineps.drv/vertical.c", 0 );
|
|
dump_intl_nls("nls/l_intl.nls");
|
|
dump_norm_table( "nls/normnfc.nls" );
|
|
dump_norm_table( "nls/normnfd.nls" );
|
|
dump_norm_table( "nls/normnfkc.nls" );
|
|
dump_norm_table( "nls/normnfkd.nls" );
|
|
dump_norm_table( "nls/normidna.nls" );
|
|
my $chartypes = dump_sortkey_table( "nls/sortdefault.nls" );
|
|
dump_locales( "nls/locale.nls", $chartypes );
|
|
foreach my $file (@allfiles) { dump_msdata_codepage( $file ); }
|
|
dump_eucjp_codepage();
|
|
dump_timezones( "dlls/tzres/tzres.rc", @timezone_files );
|
|
dump_registry_script( "dlls/kernelbase/kernelbase.rgs", %registry_keys );
|
|
|
|
exit 0;
|
|
|
|
# Local Variables:
|
|
# compile-command: "./make_unicode"
|
|
# End:
|