200 lines
5.8 KiB
Python
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
|