do not require with python macro
[platform/upstream/rpmlint.git] / I18NCheck.py
1 # -*- coding: utf-8 -*-
2 #############################################################################
3 # File          : I18NCheck.py
4 # Package       : rpmlint
5 # Author        : Frederic Lepied
6 # Created on    : Mon Nov 22 20:02:56 1999
7 # Version       : $Id: I18NCheck.py 1798 2010-06-23 19:47:26Z scop $
8 # Purpose       : checks i18n bugs.
9 #############################################################################
10
11 import re
12
13 import rpm
14
15 from Filter import addDetails, printError, printWarning
16 from __isocodes__ import COUNTRIES, LANGUAGES
17 import AbstractCheck
18
19
20 # Associative array of invalid value => correct value
21 INCORRECT_LOCALES = {
22     'in': 'id',
23     'in_ID': 'id_ID',
24     'iw': 'he',
25     'iw_IL': 'he_IL',
26     'gr': 'el',
27     'gr_GR': 'el_GR',
28     'cz': 'cs',
29     'cz_CZ': 'cs_CZ',
30     'lug': 'lg', # 'lug' is valid, but we standardize on 2 letter codes
31     'en_UK': 'en_GB'}
32
33 package_regex = re.compile('-(' + '|'.join(LANGUAGES) + ')$')
34 locale_regex = re.compile('^(/(usr|opt/kde3|opt/gnome)/share/locale/([^/]+))/')
35 correct_subdir_regex = re.compile('^(([a-z][a-z]([a-z])?(_[A-Z][A-Z])?)([.@].*$)?)$')
36 lc_messages_regex = re.compile('/usr/share/locale/([^/]+)/LC_MESSAGES/.*(mo|po)$')
37 man_regex = re.compile('/usr(?:/share)?/man/([^/]+)/man[0-9n][^/]*/[^/]+$')
38
39 # list of exceptions
40 #
41 # note: ISO-8859-9E is non standard, ISO-8859-{6,8} are of limited use
42 # as locales (since all modern handling of bidi is based on utf-8 anyway),
43 # so they should be removed once UTF-8 is deployed)
44 EXCEPTION_DIRS = ('C', 'POSIX', 'CP1251', 'CP1255', 'CP1256',
45 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5',
46 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-9E',
47 'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15',
48 'KOI8-R', 'KOI8-U', 'UTF-8', 'default')
49
50 def is_valid_lang(lang):
51     # TODO: @Foo and charset handling
52     lang = re.sub("[@.].*$", "", lang)
53
54     if lang in LANGUAGES:
55         return True
56
57     ix = lang.find("_")
58     if ix == -1:
59         return False
60
61     # TODO: don't accept all lang_COUNTRY combinations
62
63     country = lang[ix+1:]
64     if country not in COUNTRIES:
65         return False
66
67     lang = lang[0:ix]
68     if lang not in LANGUAGES:
69         return False
70
71     return True
72
73 class I18NCheck(AbstractCheck.AbstractCheck):
74
75     def __init__(self):
76         AbstractCheck.AbstractCheck.__init__(self, 'I18NCheck')
77
78     def check(self, pkg):
79
80         if pkg.isSource():
81             return
82
83         files = pkg.files().keys()
84         files.sort()
85         locales = []                      # list of locales for this packages
86         webapp = False
87
88         i18n_tags = pkg[rpm.RPMTAG_HEADERI18NTABLE] or ()
89
90         for i in i18n_tags:
91             try:
92                 correct = INCORRECT_LOCALES[i]
93                 printError(pkg, 'incorrect-i18n-tag-' + correct, i)
94             except KeyError:
95                 pass
96
97         # as some webapps have their files under /var/www/html, and
98         # others in /usr/share or /usr/lib, the only reliable way
99         # sofar to detect them is to look for an apache configuration file
100         for f in files:
101             if f.startswith('/etc/apache2/') or \
102                     f.startswith('/etc/httpd/conf.d/'):
103                 webapp = True
104
105         for f in files:
106             res = locale_regex.search(f)
107             if res:
108                 locale = res.group(2)
109                 # checks the same locale only once
110                 if locale not in locales:
111                     locales.append(locale)
112                     res2 = correct_subdir_regex.search(locale)
113                     if not res2:
114                         if locale not in EXCEPTION_DIRS:
115                             printError(pkg, 'incorrect-locale-subdir', f)
116                     else:
117                         locale_name = res2.group(2)
118                         try:
119                             correct = INCORRECT_LOCALES[locale_name]
120                             printError(pkg, 'incorrect-locale-' + correct, f)
121                         except KeyError:
122                             pass
123             res = lc_messages_regex.search(f)
124             subdir = None
125             if res:
126                 subdir = res.group(1)
127                 if not is_valid_lang(subdir):
128                     printError(pkg, 'invalid-lc-messages-dir', f)
129             else:
130                 res = man_regex.search(f)
131                 if res:
132                     subdir = res.group(1)
133                     if is_valid_lang(subdir):
134                         subdir = None
135                     else:
136                         printError(pkg, 'invalid-locale-man-dir', f)
137
138             if f.endswith('.mo') or subdir:
139                 if pkg.files()[f].lang == '' and not webapp:
140                     printWarning(pkg, 'file-not-in-%lang', f)
141
142         main_dir, main_lang = ("", "")
143         for f in files:
144             lang = pkg.files()[f].lang
145             if main_lang and lang == "" and is_prefix(main_dir + '/', f):
146                 printError(pkg, 'subfile-not-in-%lang', f)
147             if main_lang != lang:
148                 main_dir, main_lang = f, lang
149
150         name = pkg.name
151         res = package_regex.search(name)
152         if res:
153             locales = 'locales-' + res.group(1)
154             if locales != name:
155                 if locales not in (x[0] for x in pkg.requires()):
156                     printError(pkg, 'no-dependency-on', locales)
157
158 def is_prefix(p, s):
159     return len(p) <= len(s) and p == s[:len(p)]
160
161 # Create an object to enable the auto registration of the test
162 check = I18NCheck()
163
164 addDetails(
165 # Need to add a function to list all the locales
166 'incorrect-i18n-tag-',
167 """
168 """,
169
170 'incorrect-locale-subdir',
171 """
172 """,
173
174 'incorrect-locale-',
175 """
176 """,
177
178 'invalid-lc-messages-dir',
179 """
180 """,
181
182 'invalid-locale-man-dir',
183 """
184 """,
185
186 'file-not-in-lang',
187 """
188 """,
189
190 'no-dependency-on',
191 """
192 """,
193
194 'subfile-not-in-%lang',
195 """ If /foo/bar is not tagged %lang(XX) whereas /foo is, the package won't be
196 installable if XX is not in %_install_langs.""",
197
198 )
199
200 # I18NCheck.py ends here
201
202 # Local variables:
203 # indent-tabs-mode: nil
204 # py-indent-offset: 4
205 # End:
206 # ex: ts=4 sw=4 et