• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2"""
3    Convert the X11 locale.alias file into a mapping dictionary suitable
4    for locale.py.
5
6    Written by Marc-Andre Lemburg <mal@genix.com>, 2004-12-10.
7
8"""
9import locale
10import sys
11_locale = locale
12
13# Location of the X11 alias file.
14LOCALE_ALIAS = '/usr/share/X11/locale/locale.alias'
15# Location of the glibc SUPPORTED locales file.
16SUPPORTED = '/usr/share/i18n/SUPPORTED'
17
18def parse(filename):
19
20    with open(filename, encoding='latin1') as f:
21        lines = list(f)
22    # Remove mojibake in /usr/share/X11/locale/locale.alias.
23    # b'\xef\xbf\xbd' == '\ufffd'.encode('utf-8')
24    lines = [line for line in lines if '\xef\xbf\xbd' not in line]
25    data = {}
26    for line in lines:
27        line = line.strip()
28        if not line:
29            continue
30        if line[:1] == '#':
31            continue
32        locale, alias = line.split()
33        # Fix non-standard locale names, e.g. ks_IN@devanagari.UTF-8
34        if '@' in alias:
35            alias_lang, _, alias_mod = alias.partition('@')
36            if '.' in alias_mod:
37                alias_mod, _, alias_enc = alias_mod.partition('.')
38                alias = alias_lang + '.' + alias_enc + '@' + alias_mod
39        # Strip ':'
40        if locale[-1] == ':':
41            locale = locale[:-1]
42        # Lower-case locale
43        locale = locale.lower()
44        # Ignore one letter locale mappings (except for 'c')
45        if len(locale) == 1 and locale != 'c':
46            continue
47        # Normalize encoding, if given
48        if '.' in locale:
49            lang, encoding = locale.split('.')[:2]
50            encoding = encoding.replace('-', '')
51            encoding = encoding.replace('_', '')
52            locale = lang + '.' + encoding
53        data[locale] = alias
54    return data
55
56def parse_glibc_supported(filename):
57
58    with open(filename, encoding='latin1') as f:
59        lines = list(f)
60    data = {}
61    for line in lines:
62        line = line.strip()
63        if not line:
64            continue
65        if line[:1] == '#':
66            continue
67        line = line.replace('/', ' ').strip()
68        line = line.rstrip('\\').rstrip()
69        words = line.split()
70        if len(words) != 2:
71            continue
72        alias, alias_encoding = words
73        # Lower-case locale
74        locale = alias.lower()
75        # Normalize encoding, if given
76        if '.' in locale:
77            lang, encoding = locale.split('.')[:2]
78            encoding = encoding.replace('-', '')
79            encoding = encoding.replace('_', '')
80            locale = lang + '.' + encoding
81        # Add an encoding to alias
82        alias, _, modifier = alias.partition('@')
83        alias = _locale._replace_encoding(alias, alias_encoding)
84        if modifier and not (modifier == 'euro' and alias_encoding == 'ISO-8859-15'):
85            alias += '@' + modifier
86        data[locale] = alias
87    return data
88
89def pprint(data):
90    items = sorted(data.items())
91    for k, v in items:
92        print('    %-40s%a,' % ('%a:' % k, v))
93
94def print_differences(data, olddata):
95    items = sorted(olddata.items())
96    for k, v in items:
97        if k not in data:
98            print('#    removed %a' % k)
99        elif olddata[k] != data[k]:
100            print('#    updated %a -> %a to %a' % \
101                  (k, olddata[k], data[k]))
102        # Additions are not mentioned
103
104def optimize(data):
105    locale_alias = locale.locale_alias
106    locale.locale_alias = data.copy()
107    for k, v in data.items():
108        del locale.locale_alias[k]
109        if locale.normalize(k) != v:
110            locale.locale_alias[k] = v
111    newdata = locale.locale_alias
112    errors = check(data)
113    locale.locale_alias = locale_alias
114    if errors:
115        sys.exit(1)
116    return newdata
117
118def check(data):
119    # Check that all alias definitions from the X11 file
120    # are actually mapped to the correct alias locales.
121    errors = 0
122    for k, v in data.items():
123        if locale.normalize(k) != v:
124            print('ERROR: %a -> %a != %a' % (k, locale.normalize(k), v),
125                  file=sys.stderr)
126            errors += 1
127    return errors
128
129if __name__ == '__main__':
130    import argparse
131    parser = argparse.ArgumentParser()
132    parser.add_argument('--locale-alias', default=LOCALE_ALIAS,
133                        help='location of the X11 alias file '
134                             '(default: %a)' % LOCALE_ALIAS)
135    parser.add_argument('--glibc-supported', default=SUPPORTED,
136                        help='location of the glibc SUPPORTED locales file '
137                             '(default: %a)' % SUPPORTED)
138    args = parser.parse_args()
139
140    data = locale.locale_alias.copy()
141    data.update(parse_glibc_supported(args.glibc_supported))
142    data.update(parse(args.locale_alias))
143    while True:
144        # Repeat optimization while the size is decreased.
145        n = len(data)
146        data = optimize(data)
147        if len(data) == n:
148            break
149    print_differences(data, locale.locale_alias)
150    print()
151    print('locale_alias = {')
152    pprint(data)
153    print('}')
154