rpmlint/I18NCheck.py

200 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
#############################################################################
# File : I18NCheck.py
# Package : rpmlint
# Author : Frederic Lepied
# Created on : Mon Nov 22 20:02:56 1999
# Purpose : checks i18n bugs.
#############################################################################
import re
import rpm
from __isocodes__ import COUNTRIES, LANGUAGES
import AbstractCheck
from Filter import addDetails, printError, printWarning
# Associative array of invalid value => correct value
INCORRECT_LOCALES = {
'in': 'id',
'in_ID': 'id_ID',
'iw': 'he',
'iw_IL': 'he_IL',
'gr': 'el',
'gr_GR': 'el_GR',
'cz': 'cs',
'cz_CZ': 'cs_CZ',
'lug': 'lg', # 'lug' is valid, but we standardize on 2 letter codes
'en_UK': 'en_GB'}
package_regex = re.compile('-(' + '|'.join(LANGUAGES) + ')$')
locale_regex = re.compile('^(/usr/share/locale/([^/]+))/')
correct_subdir_regex = re.compile('^(([a-z][a-z]([a-z])?(_[A-Z][A-Z])?)([.@].*$)?)$')
lc_messages_regex = re.compile('/usr/share/locale/([^/]+)/LC_MESSAGES/.*(mo|po)$')
man_regex = re.compile('/usr(?:/share)?/man/([^/]+)/man[0-9n][^/]*/[^/]+$')
# list of exceptions
#
# note: ISO-8859-9E is non standard, ISO-8859-{6,8} are of limited use
# as locales (since all modern handling of bidi is based on utf-8 anyway),
# so they should be removed once UTF-8 is deployed)
EXCEPTION_DIRS = (
'C', 'POSIX', 'CP1251', 'CP1255', 'CP1256',
'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5',
'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-9E',
'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15',
'KOI8-R', 'KOI8-U', 'UTF-8', 'default')
def is_valid_lang(lang):
# TODO: @Foo and charset handling
lang = re.sub("[@.].*$", "", lang)
if lang in LANGUAGES:
return True
ix = lang.find("_")
if ix == -1:
return False
# TODO: don't accept all lang_COUNTRY combinations
country = lang[ix + 1:]
if country not in COUNTRIES:
return False
lang = lang[0:ix]
if lang not in LANGUAGES:
return False
return True
class I18NCheck(AbstractCheck.AbstractCheck):
def __init__(self):
AbstractCheck.AbstractCheck.__init__(self, 'I18NCheck')
def check_binary(self, pkg):
files = list(pkg.files().keys())
files.sort()
locales = [] # list of locales for this packages
webapp = False
i18n_tags = pkg[rpm.RPMTAG_HEADERI18NTABLE] or ()
for i in i18n_tags:
try:
correct = INCORRECT_LOCALES[i]
printError(pkg, 'incorrect-i18n-tag-' + correct, i)
except KeyError:
pass
# as some webapps have their files under /var/www/html, and
# others in /usr/share or /usr/lib, the only reliable way
# sofar to detect them is to look for an apache configuration file
for f in files:
if f.startswith('/etc/apache2/') or \
f.startswith('/etc/httpd/conf.d/'):
webapp = True
for f in files:
res = locale_regex.search(f)
if res:
locale = res.group(2)
# checks the same locale only once
if locale not in locales:
locales.append(locale)
res2 = correct_subdir_regex.search(locale)
if not res2:
if locale not in EXCEPTION_DIRS:
printError(pkg, 'incorrect-locale-subdir', f)
else:
locale_name = res2.group(2)
try:
correct = INCORRECT_LOCALES[locale_name]
printError(pkg, 'incorrect-locale-' + correct, f)
except KeyError:
pass
res = lc_messages_regex.search(f)
subdir = None
if res:
subdir = res.group(1)
if not is_valid_lang(subdir):
printError(pkg, 'invalid-lc-messages-dir', f)
else:
res = man_regex.search(f)
if res:
subdir = res.group(1)
if is_valid_lang(subdir):
subdir = None
else:
printError(pkg, 'invalid-locale-man-dir', f)
if f.endswith('.mo') or subdir:
if pkg.files()[f].lang == '' and not webapp:
printWarning(pkg, 'file-not-in-%lang', f)
main_dir, main_lang = ("", "")
for f in files:
lang = pkg.files()[f].lang
if main_lang and lang == "" and is_prefix(main_dir + '/', f):
printError(pkg, 'subfile-not-in-%lang', f)
if main_lang != lang:
main_dir, main_lang = f, lang
name = pkg.name
res = package_regex.search(name)
if res:
locales = 'locales-' + res.group(1)
if locales != name:
if locales not in (x[0] for x in pkg.requires()):
printError(pkg, 'no-dependency-on', locales)
def is_prefix(p, s):
return len(p) <= len(s) and p == s[:len(p)]
# Create an object to enable the auto registration of the test
check = I18NCheck()
addDetails(
# Need to add a function to list all the locales
'incorrect-i18n-tag-',
"""
""",
'incorrect-locale-subdir',
"""
""",
'incorrect-locale-',
"""
""",
'invalid-lc-messages-dir',
"""
""",
'invalid-locale-man-dir',
"""
""",
'file-not-in-lang',
"""
""",
'no-dependency-on',
"""
""",
'subfile-not-in-%lang',
""" If /foo/bar is not tagged %lang(XX) whereas /foo is, the package won't be
installable if XX is not in %_install_langs.""",
)
# I18NCheck.py ends here