Localisation layer for Bugzilla, separated from templates
Languages are put into i18n/<lang>/ and consist of: * messages.pl - messages for translating templates, substituted at compile time * runtime.pl - messages that are needed in runtime, setup/strings.txt.pl is moved here * messages.js - messages for JavaScripts Templates aren't subdivided into languages anymore; the single 'template/localized' directory is used, and [% L('Message text') %] or [% L('Message $1 text is $2', 'Param1', 'Param2') %] is used in the templates. Both forms are substituted at template compile time. Localised templates are auto-generated from English ones by the contrib/extract-strings.pl script which also uses i18n/en/blacklist.pl to determine what strings shouldn't be translated. So, the commit includes: * contrib/extract-strings.pl -- script for translating TT templates * i18n/en/blacklist.pl -- blacklist of messages that should not be auto-extracted * Template adjustments for more correct string extraction (for example, TT does not allow expressions in hash keys) * Template adjustments for string extraction from JavaScript blocks * js/*.js adjustments for using i18n messages * Compile-time i18n message substitution implementation * Bugzilla::Language class that manages loading of i18n messagesi18n
parent
349af79c2e
commit
8432b888fe
77
Bugzilla.pm
77
Bugzilla.pm
|
@ -36,6 +36,7 @@ use Bugzilla::DB;
|
|||
use Bugzilla::Install::Localconfig qw(read_localconfig);
|
||||
use Bugzilla::Install::Requirements qw(OPTIONAL_MODULES);
|
||||
use Bugzilla::Install::Util;
|
||||
use Bugzilla::Language;
|
||||
use Bugzilla::Template;
|
||||
use Bugzilla::User;
|
||||
use Bugzilla::Error;
|
||||
|
@ -329,22 +330,40 @@ sub send_mail
|
|||
}
|
||||
}
|
||||
|
||||
sub i18n
|
||||
{
|
||||
my $class = shift;
|
||||
my $cache = $class->request_cache;
|
||||
if (!$cache->{i18n})
|
||||
{
|
||||
# First try to check language cookie, then user preference, then Accept-Language
|
||||
# Accept-Language will be checked by Bugzilla::Language itself
|
||||
my $user = $class->login(LOGIN_OPTIONAL);
|
||||
my $lang = $class->cookies->{LANG} || ($user && $user->{settings}->{lang}->{value});
|
||||
$cache->{i18n} = Bugzilla::Language->new({ selected_language => $lang });
|
||||
}
|
||||
return $cache->{i18n};
|
||||
}
|
||||
|
||||
sub messages
|
||||
{
|
||||
my $class = shift;
|
||||
return $class->i18n->runtime_messages;
|
||||
}
|
||||
|
||||
sub template
|
||||
{
|
||||
my $class = shift;
|
||||
$class->request_cache->{language} = "";
|
||||
$class->request_cache->{template} ||= Bugzilla::Template->create();
|
||||
return $class->request_cache->{template};
|
||||
my $lang = $class->i18n->language;
|
||||
return $class->request_cache->{template}->{$lang} ||= Bugzilla::Template->create(language => $lang);
|
||||
}
|
||||
|
||||
sub template_inner
|
||||
{
|
||||
my ($class, $lang) = @_;
|
||||
$lang = defined($lang) ? $lang : ($class->request_cache->{language} || "");
|
||||
$class->request_cache->{language} = $lang;
|
||||
$class->request_cache->{"template_inner_$lang"}
|
||||
||= Bugzilla::Template->create();
|
||||
return $class->request_cache->{"template_inner_$lang"};
|
||||
$lang = $lang || $class->request_cache->{templater_inner_lang} || $class->i18n->language;
|
||||
$class->request_cache->{template_inner_lang} = $lang;
|
||||
return $class->request_cache->{template}->{$lang} ||= Bugzilla::Template->create(language => $lang);
|
||||
}
|
||||
|
||||
sub session
|
||||
|
@ -397,7 +416,6 @@ sub save_session_data
|
|||
);
|
||||
}
|
||||
|
||||
our $extension_packages;
|
||||
sub extensions
|
||||
{
|
||||
my ($class) = @_;
|
||||
|
@ -569,8 +587,7 @@ sub login
|
|||
$type = $class->params->{requirelogin} ? LOGIN_REQUIRED : LOGIN_NORMAL;
|
||||
}
|
||||
|
||||
# Allow templates to know that we're in a page that always requires
|
||||
# login.
|
||||
# Allow templates to know that we're in a page that always requires login.
|
||||
if ($type == LOGIN_REQUIRED)
|
||||
{
|
||||
$class->request_cache->{page_requires_login} = 1;
|
||||
|
@ -710,27 +727,6 @@ sub dbh_main
|
|||
return $class->request_cache->{dbh_main};
|
||||
}
|
||||
|
||||
sub languages
|
||||
{
|
||||
my $class = shift;
|
||||
return $class->request_cache->{languages}
|
||||
if $class->request_cache->{languages};
|
||||
|
||||
my @files = glob(catdir(bz_locations->{templatedir}, '*'));
|
||||
my @languages;
|
||||
foreach my $dir_entry (@files)
|
||||
{
|
||||
# It's a language directory only if it contains "default" or
|
||||
# "custom". This auto-excludes CVS directories as well.
|
||||
next unless (-d catdir($dir_entry, 'default') || -d catdir($dir_entry, 'custom'));
|
||||
$dir_entry = basename($dir_entry);
|
||||
# Check for language tag format conforming to RFC 1766.
|
||||
next unless $dir_entry =~ /^[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?$/;
|
||||
push(@languages, $dir_entry);
|
||||
}
|
||||
return $class->request_cache->{languages} = \@languages;
|
||||
}
|
||||
|
||||
sub error_mode
|
||||
{
|
||||
my ($class, $newval) = @_;
|
||||
|
@ -840,23 +836,6 @@ sub switch_to_main_db
|
|||
return $class->dbh_main;
|
||||
}
|
||||
|
||||
sub messages
|
||||
{
|
||||
my $class = shift;
|
||||
my $lc = $class->cookies->{LANG} || 'en';
|
||||
$lc =~ s/\W+//so;
|
||||
if (!$INC{'Bugzilla::Language::'.$lc})
|
||||
{
|
||||
eval { require 'Bugzilla/Language/'.$lc.'.pm' };
|
||||
if ($@)
|
||||
{
|
||||
$lc = 'en';
|
||||
require 'Bugzilla/Language/en.pm';
|
||||
}
|
||||
}
|
||||
return $Bugzilla::messages->{$lc};
|
||||
}
|
||||
|
||||
sub cache_fields
|
||||
{
|
||||
my $class = shift;
|
||||
|
|
|
@ -157,8 +157,6 @@ sub SendFlag
|
|||
my $message;
|
||||
$template->process("request/email.txt.tmpl", $email, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
Bugzilla->template_inner("");
|
||||
MessageToMTA($message);
|
||||
|
||||
Bugzilla::Hook::process('flag-notify-post-send', { vars => $email });
|
||||
|
@ -184,7 +182,6 @@ sub SendVotesRemoved
|
|||
MessageToMTA($msg);
|
||||
push @to, $voter->login;
|
||||
}
|
||||
Bugzilla->template_inner('');
|
||||
|
||||
return { sent => \@to, excluded => [] };
|
||||
}
|
||||
|
@ -649,9 +646,8 @@ sub sendMail
|
|||
|
||||
my $template = Bugzilla->template_inner($user->settings->{lang}->{value});
|
||||
Bugzilla::Hook::process('bugmail-pre_template', { tmpl => \$tmpl, vars => $vars });
|
||||
$tmpl = "email/newchangedmail.txt.tmpl" unless $template->template_exists($tmpl);
|
||||
$template->process($tmpl, $vars, \$msg) || ThrowTemplateError($template->error());
|
||||
Bugzilla->template_inner("");
|
||||
$template->process("email/newchangedmail.txt.tmpl", $vars, \$msg)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
logMail($vars);
|
||||
MessageToMTA($msg);
|
||||
|
|
|
@ -117,6 +117,8 @@ use Cwd qw(abs_path);
|
|||
SENDMAIL_EXE
|
||||
SENDMAIL_PATH
|
||||
|
||||
FIELD_TYPE_NAMES
|
||||
|
||||
FIELD_TYPE_UNKNOWN
|
||||
FIELD_TYPE_FREETEXT
|
||||
FIELD_TYPE_SINGLE_SELECT
|
||||
|
@ -393,6 +395,21 @@ use constant FIELD_TYPE_NUMERIC => 30;
|
|||
use constant FIELD_TYPE_EXTURL => 31;
|
||||
use constant FIELD_TYPE_BUG_ID_REV => 32;
|
||||
|
||||
use constant FIELD_TYPE_NAMES => {
|
||||
FIELD_TYPE_UNKNOWN() => 'UNKNOWN',
|
||||
FIELD_TYPE_FREETEXT() => 'FREETEXT',
|
||||
FIELD_TYPE_SINGLE_SELECT() => 'SINGLE_SELECT',
|
||||
FIELD_TYPE_MULTI_SELECT() => 'MULTI_SELECT',
|
||||
FIELD_TYPE_TEXTAREA() => 'TEXTAREA',
|
||||
FIELD_TYPE_DATETIME() => 'DATETIME',
|
||||
FIELD_TYPE_BUG_ID() => 'BUG_ID',
|
||||
FIELD_TYPE_BUG_URLS() => 'BUG_URLS',
|
||||
FIELD_TYPE_KEYWORDS() => 'KEYWORDS',
|
||||
FIELD_TYPE_NUMERIC() => 'NUMERIC',
|
||||
FIELD_TYPE_EXTURL() => 'EXTURL',
|
||||
FIELD_TYPE_BUG_ID_REV() => 'BUG_ID_REV',
|
||||
};
|
||||
|
||||
use constant FLAG_VISIBLE => 0;
|
||||
use constant FLAG_NULLABLE => -1;
|
||||
use constant FLAG_CLONED => -2;
|
||||
|
|
|
@ -57,8 +57,7 @@ use constant SETTINGS => {
|
|||
# 2006-08-04 wurblzap@gmail.com -- Bug 322693
|
||||
skin => { subclass => 'Skin', default => 'Mozilla' },
|
||||
# 2006-12-10 LpSolit@gmail.com -- Bug 297186
|
||||
lang => { subclass => 'Lang',
|
||||
default => ${Bugzilla->languages}[0] },
|
||||
lang => { subclass => 'Lang', default => 'en' },
|
||||
# 2007-07-02 altlist@gmail.com -- Bug 225731
|
||||
quote_replies => { options => ['quoted_reply', 'simple_reply', 'off'],
|
||||
default => "quoted_reply" },
|
||||
|
|
|
@ -26,6 +26,7 @@ use strict;
|
|||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Extension;
|
||||
use Bugzilla::Language;
|
||||
|
||||
use File::Basename;
|
||||
use POSIX qw(setlocale LC_CTYPE);
|
||||
|
@ -38,7 +39,6 @@ our @EXPORT_OK = qw(
|
|||
get_version_and_os
|
||||
indicate_progress
|
||||
install_string
|
||||
include_languages
|
||||
template_include_path
|
||||
vers_cmp
|
||||
get_console_locale
|
||||
|
@ -97,21 +97,10 @@ sub indicate_progress
|
|||
sub install_string
|
||||
{
|
||||
my ($string_id, $vars) = @_;
|
||||
_cache()->{install_string_path} ||= template_include_path();
|
||||
my $path = _cache()->{install_string_path};
|
||||
|
||||
my $string_template;
|
||||
# Find the first template that defines this string.
|
||||
foreach my $dir (@$path)
|
||||
{
|
||||
my $base = "$dir/setup/strings";
|
||||
if (!defined $string_template)
|
||||
{
|
||||
$string_template = _get_string_from_file($string_id, "$base.txt.pl");
|
||||
}
|
||||
last if defined $string_template;
|
||||
}
|
||||
my $lang = (_cache()->{language} ||= Bugzilla::Language->new());
|
||||
|
||||
my $string_template = $lang->runtime_messages->{install_strings}->{$string_id};
|
||||
if (!defined $string_template)
|
||||
{
|
||||
# Don't throw an error, it's a stupid way -- <vitalif@yourcmc.ru>
|
||||
|
@ -142,128 +131,27 @@ sub install_string
|
|||
return $string_template;
|
||||
}
|
||||
|
||||
sub include_languages
|
||||
{
|
||||
# If we are in CGI mode (not in checksetup.pl) and if the function has
|
||||
# been called without any parameter, then we cache the result of this
|
||||
# function in Bugzilla->request_cache. This is done to improve the
|
||||
# performance of the template processing.
|
||||
my $to_be_cached = 0;
|
||||
if (not @_)
|
||||
{
|
||||
my $cache = _cache();
|
||||
if (exists $cache->{include_languages})
|
||||
{
|
||||
return @{ $cache->{include_languages} };
|
||||
}
|
||||
$to_be_cached = 1;
|
||||
}
|
||||
my ($params) = @_;
|
||||
$params ||= {};
|
||||
|
||||
# Basically, the way this works is that we have a list of languages
|
||||
# that we *want*, and a list of languages that Bugzilla actually
|
||||
# supports. The caller tells us what languages they want, by setting
|
||||
# $ENV{HTTP_ACCEPT_LANGUAGE}, using the "LANG" cookie or setting
|
||||
# $params->{only_language}. The languages we support are those
|
||||
# specified in $params->{use_languages}. Otherwise we support every
|
||||
# language installed in the template/ directory.
|
||||
|
||||
my @wanted;
|
||||
if ($params->{only_language})
|
||||
{
|
||||
# We can pass several languages at once as an arrayref
|
||||
# or a single language.
|
||||
if (ref $params->{only_language})
|
||||
{
|
||||
@wanted = @{ $params->{only_language} };
|
||||
}
|
||||
else
|
||||
{
|
||||
@wanted = ($params->{only_language});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@wanted = _sort_accept_language($ENV{HTTP_ACCEPT_LANGUAGE} || '');
|
||||
# Don't use the cookie if we are in "checksetup.pl". The test
|
||||
# with $ENV{SERVER_SOFTWARE} is the same as in
|
||||
# Bugzilla:Util::i_am_cgi.
|
||||
if (exists $ENV{SERVER_SOFTWARE} && defined (my $lang = Bugzilla->cookies->{LANG}))
|
||||
{
|
||||
unshift @wanted, $lang;
|
||||
}
|
||||
}
|
||||
|
||||
my @supported;
|
||||
if (defined $params->{use_languages})
|
||||
{
|
||||
@supported = @{$params->{use_languages}};
|
||||
}
|
||||
else
|
||||
{
|
||||
my @dirs = glob(bz_locations()->{templatedir} . "/*");
|
||||
@dirs = map(basename($_), @dirs);
|
||||
@supported = grep($_ ne 'CVS', @dirs);
|
||||
}
|
||||
|
||||
my @usedlanguages;
|
||||
foreach my $wanted (@wanted)
|
||||
{
|
||||
# If we support the language we want, or *any version* of
|
||||
# the language we want, it gets pushed into @usedlanguages.
|
||||
#
|
||||
# Per RFC 1766 and RFC 2616, things like 'en' match 'en-us' and
|
||||
# 'en-uk', but not the other way around. (This is unfortunately
|
||||
# not very clearly stated in those RFC; see comment just over 14.5
|
||||
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
|
||||
if (my @found = grep /^\Q$wanted\E(-.+)?$/i, @supported)
|
||||
{
|
||||
push (@usedlanguages, @found);
|
||||
}
|
||||
}
|
||||
|
||||
# We always include English at the bottom if it's not there, even if
|
||||
# somebody removed it from use_languages.
|
||||
if (!grep($_ eq 'en', @usedlanguages))
|
||||
{
|
||||
push(@usedlanguages, 'en');
|
||||
}
|
||||
|
||||
# Cache the result if we are in CGI mode and called without parameter
|
||||
# (see the comment at the top of this function).
|
||||
if ($to_be_cached)
|
||||
{
|
||||
_cache()->{include_languages} = \@usedlanguages;
|
||||
}
|
||||
|
||||
return @usedlanguages;
|
||||
}
|
||||
|
||||
# Used by template_include_path
|
||||
sub _template_lang_directories
|
||||
sub _template_custom_directories
|
||||
{
|
||||
my ($languages, $templatedir) = @_;
|
||||
my ($templatedir) = @_;
|
||||
my @add = qw(custom default);
|
||||
my $project = bz_locations->{project};
|
||||
unshift(@add, $project) if $project;
|
||||
unshift @add, $project if $project;
|
||||
my @result;
|
||||
foreach my $lang (@$languages)
|
||||
foreach my $dir (@add)
|
||||
{
|
||||
foreach my $dir (@add)
|
||||
my $full_dir = "$templatedir/localized/$dir";
|
||||
if (-d $full_dir)
|
||||
{
|
||||
my $full_dir = "$templatedir/$lang/$dir";
|
||||
if (-d $full_dir)
|
||||
{
|
||||
trick_taint($full_dir);
|
||||
push(@result, $full_dir);
|
||||
}
|
||||
trick_taint($full_dir);
|
||||
push(@result, $full_dir);
|
||||
}
|
||||
}
|
||||
return @result;
|
||||
}
|
||||
|
||||
# Used by template_include_path.
|
||||
# Used by template_include_path
|
||||
sub _template_base_directories
|
||||
{
|
||||
my @template_dirs;
|
||||
|
@ -284,22 +172,21 @@ sub _template_base_directories
|
|||
sub template_include_path
|
||||
{
|
||||
my ($params) = @_;
|
||||
my @used_languages = include_languages($params);
|
||||
# Now, we add template directories in the order they will be searched:
|
||||
my $template_dirs = _template_base_directories();
|
||||
my @include_path;
|
||||
foreach my $template_dir (@$template_dirs)
|
||||
{
|
||||
my @lang_dirs = _template_lang_directories(\@used_languages, $template_dir);
|
||||
my @dirs = _template_custom_directories($template_dir);
|
||||
# Hooks get each set of extension directories separately.
|
||||
if ($params->{hook})
|
||||
{
|
||||
push @include_path, \@lang_dirs if @lang_dirs;
|
||||
push @include_path, \@dirs if @dirs;
|
||||
}
|
||||
# Whereas everything else just gets a whole INCLUDE_PATH.
|
||||
else
|
||||
{
|
||||
push @include_path, @lang_dirs;
|
||||
push @include_path, @dirs;
|
||||
}
|
||||
}
|
||||
# Allow to fallback to full template path - not a security risk,
|
||||
|
@ -388,33 +275,6 @@ sub _get_string_from_file
|
|||
return $strings{$string_id};
|
||||
}
|
||||
|
||||
# Make an ordered list out of a HTTP Accept-Language header (see RFC 2616, 14.4)
|
||||
# We ignore '*' and <language-range>;q=0
|
||||
# For languages with the same priority q the order remains unchanged.
|
||||
sub _sort_accept_language
|
||||
{
|
||||
my $accept_language = $_[0];
|
||||
|
||||
# clean up string.
|
||||
$accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
|
||||
my @qlanguages;
|
||||
my @languages;
|
||||
foreach(split /,/, $accept_language)
|
||||
{
|
||||
if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/)
|
||||
{
|
||||
my $lang = $1;
|
||||
my $qvalue = $2;
|
||||
$qvalue = 1 if not defined $qvalue;
|
||||
next if $qvalue == 0;
|
||||
$qvalue = 1 if $qvalue > 1;
|
||||
push @qlanguages, { qvalue => $qvalue, language => $lang };
|
||||
}
|
||||
}
|
||||
|
||||
return map($_->{language}, (sort { $b->{qvalue} <=> $a->{qvalue} } @qlanguages));
|
||||
}
|
||||
|
||||
sub get_console_locale
|
||||
{
|
||||
require Locale::Language;
|
||||
|
@ -498,24 +358,12 @@ sub _cache
|
|||
|
||||
sub trick_taint
|
||||
{
|
||||
require Carp;
|
||||
Carp::confess("Undef to trick_taint") unless defined $_[0];
|
||||
return undef unless defined $_[0];
|
||||
my $match = $_[0] =~ /^(.*)$/s;
|
||||
$_[0] = $match ? $1 : undef;
|
||||
return (defined($_[0]));
|
||||
}
|
||||
|
||||
sub trim
|
||||
{
|
||||
my ($str) = @_;
|
||||
if ($str)
|
||||
{
|
||||
$str =~ s/^\s+//g;
|
||||
$str =~ s/\s+$//g;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
|
@ -685,12 +533,6 @@ Each extension has its own directory.
|
|||
Note that languages are sorted by the user's preference (as specified
|
||||
in their browser, usually), and extensions are sorted alphabetically.
|
||||
|
||||
=item C<include_languages>
|
||||
|
||||
Used by L<Bugzilla::Template> to determine the languages' list which
|
||||
are compiled with the browser's I<Accept-Language> and the languages
|
||||
of installed templates.
|
||||
|
||||
=item C<vers_cmp>
|
||||
|
||||
=over
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
#!/usr/bin/perl
|
||||
# Localisation layer, responsible for loading i18n messages
|
||||
# FIXME: Allow extensions to have their own i18n with the same structure
|
||||
# License: Dual-license GPL 3.0+ or MPL 1.1+
|
||||
# Author: Vitaliy Filippov <vitalif@mail.ru>, 2014
|
||||
|
||||
package Bugzilla::Language;
|
||||
|
||||
use utf8;
|
||||
use strict;
|
||||
use Bugzilla::Constants;
|
||||
use File::Spec;
|
||||
use File::Basename;
|
||||
|
||||
my $message_cache = {};
|
||||
my $mtime = {};
|
||||
|
||||
# Bugzilla::Language is used in Bugzilla::Install::Util, so it shouldn't depend on Bugzilla::Util
|
||||
sub trick_taint
|
||||
{
|
||||
return undef unless defined $_[0];
|
||||
my $match = $_[0] =~ /^(.*)$/s;
|
||||
$_[0] = $match ? $1 : undef;
|
||||
return (defined($_[0]));
|
||||
}
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
$class = ref($class) || $class;
|
||||
my ($params) = @_;
|
||||
return bless { selected_language => $params->{selected_language} }, $class;
|
||||
}
|
||||
|
||||
# Make an ordered list out of a HTTP Accept-Language header (see RFC 2616, 14.4)
|
||||
# We ignore '*' and <language-range>;q=0
|
||||
# For languages with the same priority q the order remains unchanged.
|
||||
sub get_accept_language
|
||||
{
|
||||
my $self = shift;
|
||||
return $self->{accept_language} if $self->{accept_language};
|
||||
|
||||
my $accept_language = $ENV{HTTP_ACCEPT_LANGUAGE};
|
||||
|
||||
# clean up string.
|
||||
$accept_language =~ s/[^A-Za-z;q=0-9\.\-,]//g;
|
||||
my @qlanguages;
|
||||
my @languages;
|
||||
foreach (split /,/, $accept_language)
|
||||
{
|
||||
if (m/([A-Za-z\-]+)(?:;q=(\d(?:\.\d+)))?/)
|
||||
{
|
||||
my $lang = $1;
|
||||
my $qvalue = $2;
|
||||
$qvalue = 1 if not defined $qvalue;
|
||||
next if $qvalue == 0;
|
||||
$qvalue = 1 if $qvalue > 1;
|
||||
push @qlanguages, { qvalue => $qvalue, language => $lang };
|
||||
}
|
||||
}
|
||||
|
||||
return $self->{accept_language} = [ map { $_->{language} } sort { $b->{qvalue} <=> $a->{qvalue} } @qlanguages ];
|
||||
}
|
||||
|
||||
sub supported_languages
|
||||
{
|
||||
my $self = shift;
|
||||
return $self->{languages} if $self->{languages};
|
||||
my @files = glob(File::Spec->catfile(bz_locations->{cgi_path}, 'i18n', '*'));
|
||||
my @languages;
|
||||
foreach my $dir_entry (@files)
|
||||
{
|
||||
# It's a language directory only if it contains "messages.pl".
|
||||
# This auto-excludes various VCS directories as well.
|
||||
next unless -f File::Spec->catfile($dir_entry, 'messages.pl');
|
||||
$dir_entry = basename($dir_entry);
|
||||
# Check for language tag format conforming to RFC 1766.
|
||||
next unless $dir_entry =~ /^[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?$/;
|
||||
trick_taint($dir_entry);
|
||||
push @languages, $dir_entry;
|
||||
}
|
||||
return $self->{languages} = \@languages;
|
||||
}
|
||||
|
||||
sub language
|
||||
{
|
||||
my $self = shift;
|
||||
if (!$self->{language})
|
||||
{
|
||||
$self->{language} = $self->{selected_language};
|
||||
my $supported = {};
|
||||
for (@{$self->supported_languages})
|
||||
{
|
||||
$supported->{$_} = $_;
|
||||
if (/^(.*)-(.*)$/so && !$supported->{$1})
|
||||
{
|
||||
# If we support the language we want, or *any version* of
|
||||
# the language we want, it gets pushed into @usedlanguages.
|
||||
#
|
||||
# Per RFC 1766 and RFC 2616, things like 'en' match 'en-us' and
|
||||
# 'en-uk', but not the other way around. (This is unfortunately
|
||||
# not very clearly stated in those RFC; see comment just over 14.5
|
||||
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
|
||||
$supported->{$1} = $_;
|
||||
}
|
||||
}
|
||||
if (!$self->{language})
|
||||
{
|
||||
($self->{language}) = map { $supported->{$_} || () } @{$self->get_accept_language};
|
||||
}
|
||||
else
|
||||
{
|
||||
$self->{language} = $supported->{$self->{language}};
|
||||
}
|
||||
$self->{language} ||= 'en';
|
||||
}
|
||||
return $self->{language};
|
||||
}
|
||||
|
||||
sub runtime_messages
|
||||
{
|
||||
my $self = shift;
|
||||
my ($lang) = @_;
|
||||
$lang ||= $self->language;
|
||||
if (!$self->{runtime_checked}->{$lang})
|
||||
{
|
||||
my $file = File::Spec->catfile(bz_locations->{cgi_path}, 'i18n', $lang, 'runtime.pl');
|
||||
my $m = [ stat $file ]->[9];
|
||||
if (!$message_cache->{$lang} || $m && $mtime->{$lang} < $m)
|
||||
{
|
||||
$mtime->{$lang} = $m;
|
||||
$message_cache->{$lang} = do $file;
|
||||
warn $@ if $@;
|
||||
# Substitute $terms.key and $constants.key during load
|
||||
for my $sect (values %{$message_cache->{$lang} || {}})
|
||||
{
|
||||
for (values %$sect)
|
||||
{
|
||||
s/\$terms.(\w+)/$message_cache->{$lang}->{terms}->{$1}/ges;
|
||||
s/\$constants.(\w+)/eval { Bugzilla::Constants->$1() } || '$constants.'.$1/ges;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->{runtime_checked}->{$lang} = 1;
|
||||
}
|
||||
return $message_cache->{$lang} || {};
|
||||
}
|
||||
|
||||
sub template_messages
|
||||
{
|
||||
my $self = shift;
|
||||
my ($lang) = @_;
|
||||
$lang ||= $self->language;
|
||||
if (!$self->{template_messages}->{$lang})
|
||||
{
|
||||
# Template messages are needed only during compilation,
|
||||
# so they are only loaded for a single request
|
||||
my $file = File::Spec->catfile(bz_locations->{cgi_path}, 'i18n', $lang, 'messages.pl');
|
||||
if (-e $file)
|
||||
{
|
||||
$self->{template_messages}->{$lang} = do $file;
|
||||
warn $@ if $@;
|
||||
}
|
||||
}
|
||||
return $self->{template_messages}->{$lang};
|
||||
}
|
||||
|
||||
sub template_messages_mtime
|
||||
{
|
||||
my $self = shift;
|
||||
my ($lang) = @_;
|
||||
$lang ||= $self->language;
|
||||
if (!exists $self->{template_message_mtime}->{$lang})
|
||||
{
|
||||
my $file = File::Spec->catfile(bz_locations->{cgi_path}, 'i18n', $lang, 'messages.pl');
|
||||
$self->{template_message_mtime}->{$lang} = [ stat $file ]->[9];
|
||||
}
|
||||
return $self->{template_message_mtime}->{$lang};
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
|
@ -1,241 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# Internationalisation messages for English Bugzilla
|
||||
|
||||
package Bugzilla::Language::en;
|
||||
|
||||
use strict;
|
||||
use Bugzilla::Constants;
|
||||
|
||||
my $terms = {
|
||||
bug => 'bug',
|
||||
Bug => 'Bug',
|
||||
abug => 'a bug',
|
||||
Abug => 'A bug',
|
||||
aBug => 'a Bug',
|
||||
ABug => 'A Bug',
|
||||
bugs => 'bugs',
|
||||
Bugs => 'Bugs',
|
||||
zeroSearchResults => 'Zarro Boogs found',
|
||||
Bugzilla => 'Bugzilla',
|
||||
};
|
||||
|
||||
$Bugzilla::messages->{en} = {
|
||||
terms => $terms,
|
||||
operator_descs => {
|
||||
not => 'NOT',
|
||||
noop => '---',
|
||||
equals => 'is equal to',
|
||||
notequals => 'is not equal to',
|
||||
anyexact => 'is equal to any of the strings',
|
||||
substring => 'contains the string',
|
||||
notsubstring => 'does not contain the string',
|
||||
casesubstring => 'contains the string (exact case)',
|
||||
notcasesubstring => 'does not contain the string (exact case)',
|
||||
anywordssubstr => 'contains any of the strings',
|
||||
allwordssubstr => 'contains all of the strings',
|
||||
nowordssubstr => 'contains none of the strings',
|
||||
regexp => 'matches regular expression',
|
||||
notregexp => 'does not match regular expression',
|
||||
lessthan => 'is less than',
|
||||
lessthaneq => 'is less than or equal to',
|
||||
greaterthan => 'is greater than',
|
||||
greaterthaneq => 'is greater than or equal to',
|
||||
anywords => 'contains any of the words',
|
||||
allwords => 'contains all of the words',
|
||||
nowords => 'contains none of the words',
|
||||
matches => 'matches',
|
||||
notmatches => 'does not match',
|
||||
insearch => 'matched by saved search',
|
||||
notinsearch => 'not matched by saved search',
|
||||
changedbefore => 'changed before',
|
||||
changedafter => 'changed after',
|
||||
changedfrom => 'changed from',
|
||||
changedto => 'changed to',
|
||||
changedby => 'changed by',
|
||||
# Names with other_ prefix are used with correlated search terms
|
||||
other_changedbefore => 'change $1 before',
|
||||
other_changedafter => 'change $1 after',
|
||||
other_changedfrom => 'change $1 from',
|
||||
other_changedto => 'change $1 to',
|
||||
other_changedby => 'change $1 by',
|
||||
desc_between => 'between $1 and $2',
|
||||
desc_before => 'before $2',
|
||||
desc_after => 'after $1',
|
||||
desc_by => 'by $1',
|
||||
desc_fields => 'one of $1',
|
||||
},
|
||||
field_types => {
|
||||
FIELD_TYPE_UNKNOWN() => 'Unknown Type',
|
||||
FIELD_TYPE_FREETEXT() => 'Free Text',
|
||||
FIELD_TYPE_SINGLE_SELECT() => 'Drop Down',
|
||||
FIELD_TYPE_MULTI_SELECT() => 'Multiple-Selection Box',
|
||||
FIELD_TYPE_TEXTAREA() => 'Large Text Box',
|
||||
FIELD_TYPE_DATETIME() => 'Date/Time',
|
||||
FIELD_TYPE_BUG_ID() => $terms->{Bug}.' ID',
|
||||
FIELD_TYPE_BUG_URLS() => $terms->{Bug}.' URLs',
|
||||
FIELD_TYPE_KEYWORDS() => '(Invalid type) Keywords',
|
||||
FIELD_TYPE_NUMERIC() => 'Numeric',
|
||||
FIELD_TYPE_EXTURL() => 'External URL',
|
||||
FIELD_TYPE_BUG_ID_REV() => $terms->{Bug}.' ID reverse',
|
||||
},
|
||||
control_options => {
|
||||
CONTROLMAPNA() => 'NA',
|
||||
CONTROLMAPSHOWN() => 'Shown',
|
||||
CONTROLMAPDEFAULT() => 'Default',
|
||||
CONTROLMAPMANDATORY() => 'Mandatory',
|
||||
},
|
||||
field_descs => {
|
||||
alias => 'Alias',
|
||||
assigned_to => 'Assignee',
|
||||
blocked => 'Blocks',
|
||||
bug_file_loc => 'URL',
|
||||
bug_group => 'Group',
|
||||
bug_id => $terms->{Bug}.' ID',
|
||||
bug_severity => 'Severity',
|
||||
bug_status => 'Status',
|
||||
cc => 'CC',
|
||||
classification => 'Classification',
|
||||
cclist_accessible => 'CC list accessible',
|
||||
component_id => 'Component ID',
|
||||
component => 'Component',
|
||||
content => 'Content',
|
||||
comment => 'Comment',
|
||||
changes => 'Changed',
|
||||
'[Bug creation]' => 'Creation date',
|
||||
opendate => 'Creation date',
|
||||
creation_ts => 'Creation date',
|
||||
deadline => 'Deadline',
|
||||
changeddate => 'Changed',
|
||||
delta_ts => 'Changed',
|
||||
dependson => 'Depends on',
|
||||
dup_id => 'Duplicate of',
|
||||
estimated_time => 'Orig. Est.',
|
||||
everconfirmed => 'Ever confirmed',
|
||||
keywords => 'Keywords',
|
||||
newcc => 'CC',
|
||||
op_sys => 'OS',
|
||||
owner_idle_time => 'Time Since Assignee Touched',
|
||||
days_elapsed => 'Days since bug changed',
|
||||
percentage_complete => '% Complete',
|
||||
priority => 'Priority',
|
||||
product_id => 'Product ID',
|
||||
product => 'Product',
|
||||
qa_contact => 'QA Contact',
|
||||
remaining_time => 'Hours Left',
|
||||
rep_platform => 'Hardware',
|
||||
reporter => 'Reporter',
|
||||
reporter_accessible => 'Reporter accessible',
|
||||
resolution => 'Resolution',
|
||||
see_also => 'See Also',
|
||||
setting => 'Setting',
|
||||
settings => 'Settings',
|
||||
short_desc => 'Summary',
|
||||
status_whiteboard => 'Whiteboard',
|
||||
target_milestone => 'Target Milestone',
|
||||
version => 'Version',
|
||||
votes => 'Votes',
|
||||
comment0 => 'First Comment',
|
||||
interval_time => 'Period Worktime',
|
||||
work_time => 'Hours Worked',
|
||||
actual_time => 'Hours Worked',
|
||||
longdesc => 'Comment',
|
||||
commenter => 'Commenter',
|
||||
'longdescs.isprivate' => 'Comment is private',
|
||||
'attachments.description' => 'Attachment description',
|
||||
'attachments.filename' => 'Attachment filename',
|
||||
'attachments.mimetype' => 'Attachment mime type',
|
||||
'attachments.ispatch' => 'Attachment is patch',
|
||||
'attachments.isobsolete' => 'Attachment is obsolete',
|
||||
'attachments.isprivate' => 'Attachment is private',
|
||||
'attachments.submitter' => 'Attachment creator',
|
||||
'flagtypes.name' => 'Flags and Requests',
|
||||
'requestees.login_name' => 'Flag Requestee',
|
||||
'setters.login_name' => 'Flag Setter',
|
||||
# Names with other_ prefix are to be used with correlated search terms
|
||||
other_work_time => 'Hours Worked $1',
|
||||
other_longdesc => 'Comment $1',
|
||||
other_commenter => 'Comment $1 author',
|
||||
'other_longdescs.isprivate' => 'Comment $1 is private',
|
||||
'other_attachments.description' => 'Attachment $1 description',
|
||||
'other_attachments.filename' => 'Attachment $1 filename',
|
||||
'other_attachments.mimetype' => 'Attachment $1 mime type',
|
||||
'other_attachments.ispatch' => 'Attachment $1 is patch',
|
||||
'other_attachments.isobsolete' => 'Attachment $1 is obsolete',
|
||||
'other_attachments.isprivate' => 'Attachment $1 is private',
|
||||
'other_attachments.submitter' => 'Attachment $1 creator',
|
||||
'other_flagtypes.name' => 'Flag $1 Type',
|
||||
'other_requestees.login_name' => 'Flag $1 Requestee',
|
||||
'other_setters.login_name' => 'Flag $1 Setter',
|
||||
},
|
||||
setting_descs => {
|
||||
'comment_sort_order' => "When viewing $terms->{abug}, show comments in this order",
|
||||
'csv_colsepchar' => 'Field separator character for CSV files',
|
||||
'display_quips' => "Show a quip at the top of each $terms->{bug} list",
|
||||
'zoom_textareas' => 'Zoom textareas large when in use (requires JavaScript)',
|
||||
'newest_to_oldest' => 'Newest to Oldest',
|
||||
'newest_to_oldest_desc_first' => 'Newest to Oldest, but keep Description at the top',
|
||||
'off' => 'Off',
|
||||
'oldest_to_newest' => 'Oldest to Newest',
|
||||
'on' => 'On',
|
||||
'post_bug_submit_action' => "After changing $terms->{abug}",
|
||||
'next_bug' => "Show next $terms->{bug} in my list",
|
||||
'same_bug' => "Show the updated $terms->{bug}",
|
||||
'standard' => 'Classic',
|
||||
'skin' => "$terms->{Bugzilla}'s general appearance (skin)",
|
||||
'nothing' => 'Do Nothing',
|
||||
'state_addselfcc' => "Automatically add me to the CC list of $terms->{bugs} I change",
|
||||
'always' => 'Always',
|
||||
'never' => 'Never',
|
||||
'cc_unless_role' => 'Only if I have no role on them',
|
||||
'lang' => 'Language used in email',
|
||||
'quote_replies' => 'Quote the associated comment when you click on its reply link',
|
||||
'quoted_reply' => 'Quote the full comment',
|
||||
'simple_reply' => 'Reference the comment number only',
|
||||
'timezone' => 'Timezone used to display dates and times',
|
||||
'local' => 'Same as the server',
|
||||
'remind_me_about_worktime' => 'Remind me to track worktime for each comment',
|
||||
'remind_me_about_worktime_newbug' => 'Remind me to track worktime for new bugs',
|
||||
'remind_me_about_flags' => 'Remind me about flag requests',
|
||||
'saved_searches_position' => 'Position of Saved Searches bar',
|
||||
'footer' => 'Page footer',
|
||||
'header' => 'Page header',
|
||||
'both' => 'Both',
|
||||
'csv_charset' => 'Character set for CSV import and export',
|
||||
'silent_affects_flags' => 'Do not send flag email under Silent mode',
|
||||
'send' => 'Send',
|
||||
'do_not_send' => 'Don\'t send',
|
||||
'showhide_comments' => 'Which comments can be marked as collapsed',
|
||||
'none' => 'None',
|
||||
'worktime' => 'With worktime only',
|
||||
'all' => 'All',
|
||||
'comment_width' => 'Show comments in the full screen width',
|
||||
'preview_long_comments' => 'Fold long comments',
|
||||
'clear_requests_on_close' => 'Clear flag requests when closing bugs',
|
||||
'show_gravatars' => 'Show avatar images (Gravatars)',
|
||||
},
|
||||
system_groups => {
|
||||
admin => 'Administrator group. Usually allows to access <b>all</b> administrative functions.',
|
||||
admin_index => 'Allows to <a href="admin.cgi">enter Administration area</a>, granted automatically if you can access any of the administrative functions.',
|
||||
tweakparams => 'Allows to <a href="editparams.cgi">change Parameters</a>.',
|
||||
editusers => 'Allows to <a href="editusers.cgi">edit or disable users</a> and include/exclude them from <b>all</b> groups.',
|
||||
creategroups => 'Allows to <a href="editgroups.cgi">create, destroy and edit groups</a>.',
|
||||
createproducts => 'Allows to <a href="editproducts.cgi">create new products</a>.',
|
||||
editclassifications => 'Allows to <a href="editclassifications.cgi">create, destroy and edit classifications</a>.',
|
||||
editcomponents => 'Allows to <a href="editproducts.cgi">create, destroy and edit all products, components, versions and milestones</a>.',
|
||||
editkeywords => 'Allows to <a href="editvalues.cgi?field=keywords">create, destroy and edit keywords</a>.',
|
||||
editbugs => 'Allows to edit all fields of all bugs.',
|
||||
canconfirm => 'Allows to confirm bugs or mark them as duplicates.',
|
||||
bz_canusewhineatothers => 'Allows to <a href="editwhines.cgi">configure whine reports for other users</a>.',
|
||||
bz_canusewhines => 'Allows to <a href="editwhines.cgi">configure whine reports for self</a>.',
|
||||
bz_sudoers => 'Allows to <a href="relogin.cgi?action=prepare-sudo">impersonate other users</a> and perform actions as them.',
|
||||
bz_sudo_protect => 'Forbids other users to impersonate you.',
|
||||
bz_editcheckers => 'Allows to <a href="editcheckers.cgi">edit Predicates</a> ("Correctness Checkers").',
|
||||
editfields => 'Allows to <a href="editfields.cgi">create, destroy and edit properties of bug fields</a>.',
|
||||
editvalues => 'Allows to <a href="editvalues.cgi">edit allowed values for all bug fields</a>.',
|
||||
importxls => 'Allows to <a href="importxls.cgi">create or update many bugs at once using Excel and CSV files</a>.',
|
||||
worktimeadmin => 'Allows to register working time for other users and for dates in the past (see "Fix Worktime" link under a bug list).',
|
||||
editflagtypes => 'Allows to <a href="editflagtypes.cgi">create, destroy and edit flag types</a>.',
|
||||
},
|
||||
};
|
||||
|
||||
__END__
|
|
@ -37,7 +37,7 @@ use Bugzilla::Bug;
|
|||
use Bugzilla::Constants;
|
||||
use Bugzilla::Hook;
|
||||
use Bugzilla::Install::Requirements;
|
||||
use Bugzilla::Install::Util qw(install_string template_include_path include_languages);
|
||||
use Bugzilla::Install::Util qw(install_string template_include_path);
|
||||
use Bugzilla::Keyword;
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::User;
|
||||
|
@ -45,6 +45,7 @@ use Bugzilla::Error;
|
|||
use Bugzilla::Status;
|
||||
use Bugzilla::Token;
|
||||
use Bugzilla::Template::Plugin::Bugzilla;
|
||||
use Bugzilla::Template::Plugin::Hook;
|
||||
|
||||
use Cwd qw(abs_path);
|
||||
use MIME::Base64;
|
||||
|
@ -60,6 +61,9 @@ use Scalar::Util qw(blessed);
|
|||
|
||||
use base qw(Template);
|
||||
|
||||
our $CURRENT_TEMPLATE;
|
||||
our $COMPILE_LANGUAGE;
|
||||
|
||||
my ($custom_proto, $custom_proto_regex, $custom_proto_cached, $custom_wiki_urls, $custom_wiki_proto);
|
||||
|
||||
# Convert the constants in the Bugzilla::Constants module into a hash we can
|
||||
|
@ -85,21 +89,6 @@ sub _load_constants
|
|||
return \%constants;
|
||||
}
|
||||
|
||||
# Returns the path to the templates based on the Accept-Language
|
||||
# settings of the user and of the available languages
|
||||
# If no Accept-Language is present it uses the defined default
|
||||
# Templates may also be found in the extensions/ tree
|
||||
sub getTemplateIncludePath
|
||||
{
|
||||
my $cache = Bugzilla->request_cache;
|
||||
my $lang = $cache->{language} || '';
|
||||
$cache->{"template_include_path_$lang"} ||= template_include_path({
|
||||
use_languages => Bugzilla->languages,
|
||||
only_language => $lang,
|
||||
});
|
||||
return $cache->{"template_include_path_$lang"};
|
||||
}
|
||||
|
||||
sub get_format
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -142,20 +131,6 @@ sub get_format
|
|||
};
|
||||
}
|
||||
|
||||
# check whether a template exists
|
||||
sub template_exists
|
||||
{
|
||||
my $self = shift;
|
||||
my $file = shift or return;
|
||||
my $include_path = $self->context->load_templates->[0]->include_path;
|
||||
return unless ref $include_path eq 'ARRAY';
|
||||
foreach my $path (@$include_path)
|
||||
{
|
||||
return $path if -e "$path/$file";
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub makeTables
|
||||
{
|
||||
my ($comment) = @_;
|
||||
|
@ -533,10 +508,10 @@ sub get_bug_link
|
|||
$bug = blessed($bug) ? $bug : new Bugzilla::Bug($bug);
|
||||
return $link_text if !$bug;
|
||||
|
||||
my $title = get_text('get_status', { status => $bug->bug_status_obj->name });
|
||||
my $title = $bug->bug_status_obj->name;
|
||||
if ($bug->resolution)
|
||||
{
|
||||
$title .= ' ' . get_text('get_resolution', { resolution => $bug->resolution_obj->name });
|
||||
$title .= ' ' . $bug->resolution_obj->name;
|
||||
}
|
||||
my $cansee = Bugzilla->user->can_see_bug($bug);
|
||||
if (Bugzilla->params->{unauth_bug_details} || $cansee)
|
||||
|
@ -569,6 +544,7 @@ sub get_bug_link
|
|||
use Template::Directive;
|
||||
use Template::Stash;
|
||||
use Template::Exception;
|
||||
no warnings 'redefine';
|
||||
|
||||
# The Template Toolkit throws an error if a loop iterates >1000 times.
|
||||
# We want to raise that limit.
|
||||
|
@ -576,6 +552,51 @@ use Template::Exception;
|
|||
# If you do not re-run checksetup.pl, the change you make will not apply
|
||||
$Template::Directive::WHILE_MAX = 1000000;
|
||||
|
||||
# Override ident() to allow compile-time substitution of i18n strings
|
||||
our $old_ident;
|
||||
$old_ident ||= \&Template::Directive::ident;
|
||||
*Template::Directive::ident = sub
|
||||
{
|
||||
my ($self, $ident) = @_;
|
||||
if (@$ident == 2 && $ident->[0] eq "'L'" &&
|
||||
$ident->[1] =~ /^\s*\[\s*\'((?:[^\\\']+|\\.)+)\'\s*(,.+)?\]\s*$/so)
|
||||
{
|
||||
my $id = $1;
|
||||
my $params = $2;
|
||||
$id =~ s/\\(.)/$1/gso;
|
||||
my $msg = Bugzilla->i18n->template_messages($COMPILE_LANGUAGE)->{$CURRENT_TEMPLATE};
|
||||
my $rtmsg;
|
||||
$msg = $msg->{$id} || $id;
|
||||
$msg =~ s/\$terms.(\w+)/$rtmsg ||= Bugzilla->i18n->runtime_messages($COMPILE_LANGUAGE); $rtmsg->{terms}->{$1}/ges;
|
||||
$msg =~ s/\$constants.(\w+)/eval { Bugzilla::Constants->$1() || "<Unknown Constant>" }/ges;
|
||||
$msg =~ s/([\\\'])/\\$1/gso;
|
||||
$msg = "'$msg'";
|
||||
if ($params)
|
||||
{
|
||||
$params =~ s/^[,\s]+//so;
|
||||
$params =~ s/[,\s]+$//so;
|
||||
my @a;
|
||||
while ($params =~ s/^((?:
|
||||
[^,\(\)\{\}\[\]\"\']+ |
|
||||
\"(?:(?:[^\\\"]+|\\.)*)\" |
|
||||
\'(?:(?:[^\\\']+|\\.)*)\' |
|
||||
\((?:(?1),?)*\) |
|
||||
\{(?:(?1),?)*\} |
|
||||
\[(?:(?1),?)*\]
|
||||
)+),?\s*//xiso)
|
||||
{
|
||||
push @a, $1;
|
||||
}
|
||||
if ($msg =~ s/\$(\d+)/"'.".$a[$1-1].".'"/ges)
|
||||
{
|
||||
$msg = "($msg)";
|
||||
}
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
return &$old_ident($self, $ident);
|
||||
};
|
||||
|
||||
# Allow keys to start with an underscore or a dot.
|
||||
$Template::Stash::PRIVATE = undef;
|
||||
|
||||
|
@ -642,12 +663,16 @@ sub create
|
|||
my $class = shift;
|
||||
my %opts = @_;
|
||||
|
||||
$opts{language} ||= 'en';
|
||||
|
||||
my $lc_messages = Bugzilla->i18n->runtime_messages($opts{language});
|
||||
|
||||
# IMPORTANT - If you make any FILTER changes here, make sure to
|
||||
# make them in t/004.template.t also, if required.
|
||||
|
||||
my $config = {
|
||||
# Colon-separated list of directories containing templates.
|
||||
INCLUDE_PATH => $opts{include_path} || getTemplateIncludePath(),
|
||||
INCLUDE_PATH => $opts{include_path} || template_include_path(),
|
||||
|
||||
# Remove white-space before template directives (PRE_CHOMP) and at the
|
||||
# beginning and end of templates and template blocks (TRIM) for better
|
||||
|
@ -664,10 +689,7 @@ sub create
|
|||
ABSOLUTE => 1,
|
||||
RELATIVE => $ENV{MOD_PERL} ? 0 : 1,
|
||||
|
||||
COMPILE_DIR => bz_locations()->{datadir} . "/template",
|
||||
|
||||
# Initialize templates (f.e. by loading plugins like Hook).
|
||||
PRE_PROCESS => ["global/initialize.none.tmpl"],
|
||||
COMPILE_DIR => bz_locations()->{datadir} . "/template/" . $opts{language},
|
||||
|
||||
ENCODING => Bugzilla->params->{utf8} ? 'UTF-8' : undef,
|
||||
|
||||
|
@ -944,14 +966,21 @@ sub create
|
|||
|
||||
PLUGIN_BASE => 'Bugzilla::Template::Plugin',
|
||||
|
||||
CONSTANTS => _load_constants(),
|
||||
|
||||
# Default variables for all templates
|
||||
VARIABLES => {
|
||||
terms => Bugzilla->messages->{terms},
|
||||
field_descs => Bugzilla->messages->{field_descs},
|
||||
lc_messages => Bugzilla->messages,
|
||||
LANGUAGE => $opts{language},
|
||||
Bugzilla => Bugzilla::Template::Plugin::Bugzilla->new,
|
||||
constants => _load_constants(),
|
||||
|
||||
terms => $lc_messages->{terms},
|
||||
field_descs => $lc_messages->{field_descs},
|
||||
lc_messages => $lc_messages,
|
||||
|
||||
# All template messages should be substituted during compilation
|
||||
L => sub { die "Non-constant message keys are not supported" },
|
||||
|
||||
# html_quote in the form of a function
|
||||
html => \&html_quote,
|
||||
|
||||
# html_quote in the form of a function
|
||||
html => \&html_quote,
|
||||
|
@ -1020,14 +1049,6 @@ sub create
|
|||
# If an sudo session is in progress, this is the user we're faking
|
||||
user => sub { return Bugzilla->user; },
|
||||
|
||||
# Currenly active language
|
||||
# FIXME Eventually this should probably be replaced with something like Bugzilla->language.
|
||||
current_language => sub
|
||||
{
|
||||
my ($language) = include_languages();
|
||||
return $language;
|
||||
},
|
||||
|
||||
# If an sudo session is in progress, this is the user who
|
||||
# started the session.
|
||||
sudoer => sub { return Bugzilla->sudoer; },
|
||||
|
@ -1036,10 +1057,16 @@ sub create
|
|||
urlbase => sub { return Bugzilla::Util::correct_urlbase(); },
|
||||
|
||||
# Allow templates to access docs url with users' preferred language
|
||||
docs_urlbase => sub {
|
||||
my ($language) = include_languages();
|
||||
docs_urlbase => sub
|
||||
{
|
||||
my $docs_urlbase = Bugzilla->params->{docs_urlbase};
|
||||
$docs_urlbase =~ s/\%lang\%/$language/;
|
||||
my $lang = $opts{language};
|
||||
# FIXME maybe use accept_languages?
|
||||
if (!-d File::Spec->catfile(bz_locations()->{cgi_dir}, 'docs', $lang))
|
||||
{
|
||||
$lang = 'en';
|
||||
}
|
||||
$docs_urlbase =~ s/\%lang\%/$lang/;
|
||||
return $docs_urlbase;
|
||||
},
|
||||
|
||||
|
@ -1089,15 +1116,18 @@ sub create
|
|||
};
|
||||
|
||||
local $Template::Config::CONTEXT = 'Bugzilla::Template::Context';
|
||||
local $Template::Config::PROVIDER = 'Bugzilla::Template::Provider';
|
||||
|
||||
Bugzilla::Hook::process('template_before_create', { config => $config });
|
||||
my $template = $class->new($config)
|
||||
|| die("Template creation failed: " . $class->error());
|
||||
my $template = $class->new($config) || die("Template creation failed: " . $class->error());
|
||||
|
||||
$template->context->{CONFIG}->{VARIABLES}->{Hook} = Bugzilla::Template::Plugin::Hook->new($template->context);
|
||||
$template->{bz_language} = $template->context->{bz_language} = $opts{language};
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
# Used as part of the two subroutines below.
|
||||
our %_templates_to_precompile;
|
||||
sub precompile_templates
|
||||
{
|
||||
my ($output) = @_;
|
||||
|
@ -1126,21 +1156,34 @@ sub precompile_templates
|
|||
|
||||
print install_string('template_precompile') if $output;
|
||||
|
||||
my $paths = template_include_path({
|
||||
use_languages => Bugzilla->languages,
|
||||
only_language => Bugzilla->languages,
|
||||
});
|
||||
my $templates_to_precompile = {};
|
||||
my $paths = template_include_path();
|
||||
foreach my $dir (@$paths)
|
||||
{
|
||||
my $template = Bugzilla::Template->create(include_path => [$dir]);
|
||||
%_templates_to_precompile = ();
|
||||
# Traverse the template hierarchy.
|
||||
find({ wanted => \&_precompile_push, no_chdir => 1 }, $dir);
|
||||
find({
|
||||
wanted => sub
|
||||
{
|
||||
my $name = $File::Find::name;
|
||||
return if -d $name;
|
||||
return if $name =~ /\/(CVS|\.svn|\.git|\.hg|\.bzr)\//;
|
||||
return if $name !~ /\.tmpl$/;
|
||||
$templates_to_precompile->{substr($name, 1+length $dir)} = 1;
|
||||
},
|
||||
no_chdir => 1,
|
||||
}, $dir);
|
||||
}
|
||||
|
||||
# FIXME: Do not precompile ALL languages when we'll have more than several.
|
||||
my $languages = Bugzilla->i18n->supported_languages;
|
||||
foreach my $lang (@$languages)
|
||||
{
|
||||
my $template = Bugzilla::Template->create(language => $lang);
|
||||
# The sort isn't totally necessary, but it makes debugging easier
|
||||
# by making the templates always be compiled in the same order.
|
||||
foreach my $file (sort keys %_templates_to_precompile)
|
||||
foreach my $file (sort keys %$templates_to_precompile)
|
||||
{
|
||||
$file =~ s{^\Q$dir\E/}{};
|
||||
local $Bugzilla::Template::CURRENT_TEMPLATE = $file;
|
||||
# Compile the template but throw away the result. This has the side-
|
||||
# effect of writing the compiled version to disk.
|
||||
$template->context->template($file);
|
||||
|
@ -1166,16 +1209,6 @@ sub precompile_templates
|
|||
print install_string('done') . "\n" if $output;
|
||||
}
|
||||
|
||||
# Helper for precompile_templates
|
||||
sub _precompile_push
|
||||
{
|
||||
my $name = $File::Find::name;
|
||||
return if -d $name;
|
||||
return if $name =~ /\/(CVS|\.svn|\.git|\.hg|\.bzr)\//;
|
||||
return if $name !~ /\.tmpl$/;
|
||||
$_templates_to_precompile{$name} = 1;
|
||||
}
|
||||
|
||||
# Helper for precompile_templates
|
||||
sub _do_template_symlink
|
||||
{
|
||||
|
|
|
@ -60,6 +60,12 @@ sub process
|
|||
return $result;
|
||||
}
|
||||
|
||||
sub template {
|
||||
my $self = shift;
|
||||
local $Bugzilla::Template::COMPILE_LANGUAGE = $self->{bz_language};
|
||||
return $self->SUPER::template(@_);
|
||||
}
|
||||
|
||||
# This method is called by Template-Toolkit exactly once per template or
|
||||
# block (look at a compiled template) so this is an ideal place for us to
|
||||
# modify the variables before a template or block runs.
|
||||
|
|
|
@ -25,7 +25,7 @@ use strict;
|
|||
use base qw(Template::Plugin);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Install::Util qw(include_languages template_include_path);
|
||||
use Bugzilla::Install::Util qw(template_include_path);
|
||||
use Bugzilla::Util;
|
||||
use Bugzilla::Error;
|
||||
|
||||
|
@ -66,15 +66,12 @@ sub process
|
|||
# Hooks are named like this:
|
||||
my $extension_template = "$path$template_name-$hook_name.$type.tmpl";
|
||||
|
||||
# Get the hooks out of the cache if they exist. Otherwise, read them
|
||||
# from the disk.
|
||||
# Get the hooks out of the cache if they exist. Otherwise, read them from the disk.
|
||||
my $cache = Bugzilla->request_cache->{template_plugin_hook_cache} ||= {};
|
||||
my $lang = Bugzilla->request_cache->{language} || '';
|
||||
$cache->{"${lang}__$extension_template"}
|
||||
||= $self->_get_hooks($extension_template);
|
||||
$cache->{"${lang}__$extension_template"} ||= $self->_get_hooks($extension_template);
|
||||
|
||||
# process() accepts an arrayref of templates, so we just pass the whole
|
||||
# arrayref.
|
||||
# process() accepts an arrayref of templates, so we just pass the whole arrayref.
|
||||
$context->{bz_in_hook} = 1; # See Bugzilla::Template::Context
|
||||
return $context->process($cache->{"${lang}__$extension_template"});
|
||||
}
|
||||
|
@ -106,14 +103,8 @@ sub _get_hooks
|
|||
sub _template_hook_include_path
|
||||
{
|
||||
my $cache = Bugzilla->request_cache;
|
||||
my $language = $cache->{language} || '';
|
||||
my $cache_key = "template_plugin_hook_include_path_$language";
|
||||
$cache->{$cache_key} ||= template_include_path({
|
||||
use_languages => Bugzilla->languages,
|
||||
only_language => $language,
|
||||
hook => 1,
|
||||
});
|
||||
return $cache->{$cache_key};
|
||||
$cache->{template_hook_include_path} ||= template_include_path({ hook => 1 });
|
||||
return $cache->{template_hook_include_path};
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# Make template mtime dependent on i18n messages mtime
|
||||
# License: Dual-license MPL 1.1+ or GPL 3.0+
|
||||
# Author(s): Vitaliy Filippov <vitalif@mail.ru>
|
||||
|
||||
package Bugzilla::Template::Provider;
|
||||
|
||||
use strict;
|
||||
use base 'Template::Provider';
|
||||
|
||||
sub _template_modified
|
||||
{
|
||||
my $self = shift;
|
||||
my $template = shift || return;
|
||||
my $mtime = (stat $template)[9];
|
||||
if ($mtime)
|
||||
{
|
||||
my $msgmtime = Bugzilla->i18n->template_messages_mtime($Bugzilla::Template::COMPILE_LANGUAGE);
|
||||
$mtime = $msgmtime if $msgmtime > $mtime;
|
||||
}
|
||||
return $mtime;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
|
@ -124,7 +124,6 @@ sub IssueEmailChangeToken {
|
|||
$template->process("account/email/change-new.txt.tmpl", $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
Bugzilla->template_inner("");
|
||||
MessageToMTA($message);
|
||||
}
|
||||
|
||||
|
@ -161,7 +160,6 @@ sub IssuePasswordToken {
|
|||
$vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
Bugzilla->template_inner("");
|
||||
MessageToMTA($message);
|
||||
}
|
||||
|
||||
|
@ -302,7 +300,6 @@ sub Cancel {
|
|||
$template->process("account/cancel-token.txt.tmpl", $vars, \$message)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
Bugzilla->template_inner("");
|
||||
MessageToMTA($message);
|
||||
|
||||
# Delete the token from the database.
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
|
@ -21,21 +19,20 @@
|
|||
package Bugzilla::User::Setting::Lang;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Bugzilla::User::Setting);
|
||||
|
||||
use Bugzilla::Constants;
|
||||
|
||||
sub legal_values {
|
||||
sub legal_values
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->{'legal_values'} if defined $self->{'legal_values'};
|
||||
return $self->{legal_values} if defined $self->{legal_values};
|
||||
|
||||
return $self->{'legal_values'} = Bugzilla->languages;
|
||||
return $self->{legal_values} = Bugzilla->i18n->supported_languages;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
|
|
@ -780,6 +780,7 @@ sub clean_text
|
|||
# from quoteUrls() for each comment. This leaded to TERRIBLE performance
|
||||
# on "long" bugs compared to Bugzilla 2.x!
|
||||
|
||||
# FIXME: Rework it to just use Bugzilla::Language instead of calling templates.
|
||||
sub get_text
|
||||
{
|
||||
my ($name, $vars) = @_;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/sh
|
||||
perl checksetup.pl --no-chmod --no-templates
|
||||
perl checksetup.pl --no-chmod --no-templates $*
|
||||
|
|
|
@ -0,0 +1,363 @@
|
|||
#!/usr/bin/perl
|
||||
# Extract strings for localisation from Bugzilla Template Toolkit templates
|
||||
# and automatically replaces them with with L('...') calls.
|
||||
# Reads blacklist from i18n/en/blacklist.pl, processes all templates. Outputs:
|
||||
# 1) Translated versions of all templates to template/localized/
|
||||
# 2) Extracted messages to i18n/en/messages.pl
|
||||
# 3) For convenience, also writes messages that were added or removed since
|
||||
# the previous run to i18n/en/added.pl and i18n/en/removed.pl.
|
||||
|
||||
use strict;
|
||||
use Encode;
|
||||
use lib qw(..);
|
||||
use Cwd;
|
||||
use File::Basename qw(dirname);
|
||||
use File::Path qw(make_path);
|
||||
use File::Find;
|
||||
|
||||
my $bz_root = Cwd::abs_path(dirname(__FILE__).'/..');
|
||||
my $msg_path = "$bz_root/i18n/en";
|
||||
-d $msg_path or make_path($msg_path);
|
||||
|
||||
my $blacklist;
|
||||
|
||||
if (-f "$msg_path/blacklist.pl")
|
||||
{
|
||||
$blacklist = do "$msg_path/blacklist.pl";
|
||||
$blacklist or die "blacklist.pl should define \$blacklist = { 'template' => { 'string' => anything } };";
|
||||
}
|
||||
|
||||
my $strings = {};
|
||||
|
||||
#$File::Find::name = '/var/home/www/localhost/bugs3/template/en/default/global/header.html.tmpl';
|
||||
#process_file();
|
||||
#exit;
|
||||
|
||||
File::Find::find(\&process_file, "$bz_root/template/en", glob("$bz_root/extensions/*/template/en"));
|
||||
|
||||
my ($removed, $added) = diff_strings("$msg_path/messages.pl", $strings);
|
||||
|
||||
write_dump("$msg_path/messages.pl", $strings);
|
||||
write_dump("$msg_path/removed.pl", $removed) if $removed && %$removed;
|
||||
write_dump("$msg_path/added.pl", $added) if $added && %$added;
|
||||
|
||||
#print_duplicated($strings);
|
||||
|
||||
exit;
|
||||
|
||||
sub process_file
|
||||
{
|
||||
my $n = $File::Find::name;
|
||||
if ($n =~ /\.(txt|html)\.tmpl$/ && open FD, '<', $n)
|
||||
{
|
||||
local $/ = undef;
|
||||
my $s = <FD>;
|
||||
close FD;
|
||||
my $rel = substr($n, 1 + length $bz_root);
|
||||
$rel =~ s!^extensions/([^/]*)/template/en/(default|custom)/(.*)$!extensions/$1/$3!so;
|
||||
$rel =~ s!^template/en/(default|custom)/(.*)$!$2!so;
|
||||
($s, $strings->{$rel}) = translate_template($s, $n =~ /\.txt\.tmpl$/ ? 1 : 0, $blacklist->{$rel});
|
||||
delete $strings->{$rel} if !%{$strings->{$rel}};
|
||||
my $tr = $n;
|
||||
$tr =~ s!/en/!/localized/!;
|
||||
-d dirname($tr) or make_path(dirname($tr));
|
||||
open FD, '>', $tr or die "Can't write $tr";
|
||||
print FD $s;
|
||||
close FD;
|
||||
}
|
||||
}
|
||||
|
||||
sub diff_strings
|
||||
{
|
||||
my ($fn, $strings) = @_;
|
||||
my ($removed, $added);
|
||||
-f $fn or return;
|
||||
my $old_strings = do $fn or return;
|
||||
for (keys %$old_strings)
|
||||
{
|
||||
$strings->{$_} = $old_strings->{$_} if !/\.tmpl$/so;
|
||||
}
|
||||
my %all_keys = map { map { $_ => 1 } keys %$_ } ($old_strings, $strings);
|
||||
for my $fn (keys %all_keys)
|
||||
{
|
||||
$removed->{$fn}->{''} = [ grep { !exists $strings->{$fn}->{$_} } keys %{$old_strings->{$fn} || {}} ];
|
||||
$added->{$fn}->{''} = [ grep { !exists $old_strings->{$fn}->{$_} } @{$strings->{$fn} ? $strings->{$fn}->{''} : []} ];
|
||||
for ($removed, $added)
|
||||
{
|
||||
delete $_->{$fn} if !@{$_->{$fn}->{''}};
|
||||
}
|
||||
}
|
||||
return ($removed, $added);
|
||||
}
|
||||
|
||||
sub write_dump
|
||||
{
|
||||
my ($fn, $strings) = @_;
|
||||
my $dump = "use utf8;\n{\n";
|
||||
for my $file (sort keys %$strings)
|
||||
{
|
||||
$dump .= " '$file' => {\n";
|
||||
for my $s (@{$strings->{$file}->{''}})
|
||||
{
|
||||
$s =~ s/([\\\'])/\\$1/gso;
|
||||
$dump .= " '$s' =>\n '$s',\n";
|
||||
}
|
||||
$dump .= " },\n";
|
||||
}
|
||||
$dump .= "};\n";
|
||||
Encode::_utf8_off($dump);
|
||||
open FD, '>', $fn or die "Can't write $fn";
|
||||
print FD $dump;
|
||||
close FD;
|
||||
}
|
||||
|
||||
sub print_duplicated
|
||||
{
|
||||
my $all = {};
|
||||
for my $file (sort keys %$strings)
|
||||
{
|
||||
for my $s (@{$strings->{$file}->{''}})
|
||||
{
|
||||
push @{$all->{$s}}, $file;
|
||||
}
|
||||
}
|
||||
for (sort keys %$all)
|
||||
{
|
||||
if (@{$all->{$_}} > 1)
|
||||
{
|
||||
print "$_\n";
|
||||
for (@{$all->{$_}})
|
||||
{
|
||||
print " $_\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub translate_template
|
||||
{
|
||||
my ($template, $is_txt, $blacklist) = @_;
|
||||
my $translated = '';
|
||||
my $strings = {};
|
||||
my $last = '';
|
||||
my $last_raw = '';
|
||||
my $outtag; # translated tag/directive/whatever
|
||||
my $pre_nl; # newlines before non-special string match (to preserve them if not translating)
|
||||
while ($is_txt ? $template =~ s/^(.*?)(\[\%|<|\n\s*\n|$)//so : $template =~ s/^(.*?)(\[\%|<|$)//so)
|
||||
{
|
||||
$outtag = $pre_nl = '';
|
||||
my ($pre, $tag) = ($1, $2);
|
||||
my $pre_raw = $last_raw.$pre;
|
||||
$pre = $last.$pre;
|
||||
$last_raw = '';
|
||||
$last = '';
|
||||
$pre =~ s/\s+$//so and $outtag .= $&;
|
||||
$pre =~ s/^\s+//so and $pre_nl = $&;
|
||||
$pre_raw =~ s/^\s+//so;
|
||||
$pre_raw =~ s/\s+$//so;
|
||||
$pre =~ s/\s+/ /gso;
|
||||
if ($tag eq '[%')
|
||||
{
|
||||
if ($template =~ s/^([\-\+]?)\s*terms.(\w+)\s*-?\%\]//so)
|
||||
{
|
||||
$outtag = ' ' if $outtag;
|
||||
$last_raw = $pre_nl.$pre.$outtag.'[%'.$1.' terms.'.$2.' %]';
|
||||
$last = $pre_nl.$pre.$outtag.($1 eq '+' ? ' ' : '').'$terms.'.$2;
|
||||
next;
|
||||
}
|
||||
$outtag .= '[%' . tt_dir($template, $strings, $blacklist);
|
||||
}
|
||||
elsif ($tag eq '<')
|
||||
{
|
||||
if ($template =~ s/^(\/?)(u|b|i|strong|em)>//iso)
|
||||
{
|
||||
$outtag = ' ' if $outtag;
|
||||
$last_raw = $last = $pre_nl.$pre.$outtag.'<'.$1.$2.'>';
|
||||
next;
|
||||
}
|
||||
$outtag .= '<';
|
||||
my ($script) = $template =~ /^\s*(script|style)/iso;
|
||||
my ($tagname) = $template =~ /^\/?(\S+)/so;
|
||||
my $is_button = 0;
|
||||
while ($template =~ s/^(.*?)(\[\%|\s+|>)//so)
|
||||
{
|
||||
$outtag .= $1 . $2;
|
||||
my $d = $2;
|
||||
if ($d eq '[%')
|
||||
{
|
||||
$outtag .= tt_dir($template, $strings, $blacklist, 1);
|
||||
}
|
||||
elsif ($d eq '>')
|
||||
{
|
||||
last;
|
||||
}
|
||||
elsif ($template =~ s/^((title|alt)\s*=[\"\'])//iso ||
|
||||
$tagname eq 'input' && $template =~ s/^((type)\s*=[\"\'])//iso ||
|
||||
$is_button && $template =~ s/^((value)\s*=[\"\'])//iso)
|
||||
{
|
||||
$outtag .= $1;
|
||||
if (lc($2) eq 'type')
|
||||
{
|
||||
$is_button = $template =~ /^(submit|button)/iso ? 1 : 0;
|
||||
next;
|
||||
}
|
||||
my $last = '';
|
||||
my $last_raw = '';
|
||||
while ($template =~ s/^([^\"\']*?)(\[\%|\"|\')//so)
|
||||
{
|
||||
my $pre = $last.$1;
|
||||
my $pre_raw = $last_raw.$1;
|
||||
my $n = $2;
|
||||
my $outtag2 = '';
|
||||
$last = '';
|
||||
$last_raw = '';
|
||||
$pre =~ s/\s+/ /gso;
|
||||
$pre =~ s/^\s*//so;
|
||||
$pre_raw =~ s/^\s*//so;
|
||||
if ($n ne '"' && $n ne "'")
|
||||
{
|
||||
if ($template =~ s/^-?\s*terms.(\w+)\s*-?\%\]//so)
|
||||
{
|
||||
$last = $pre.'$terms.'.$1;
|
||||
$last_raw = $pre.'[%'.$&;
|
||||
next;
|
||||
}
|
||||
elsif ($template =~ s/^\s*\%\]//so)
|
||||
{
|
||||
# empty directive
|
||||
$last_raw = $last = $pre;
|
||||
next;
|
||||
}
|
||||
$outtag2 .= '[%' . tt_dir($template, $strings, $blacklist);
|
||||
}
|
||||
else
|
||||
{
|
||||
$outtag2 .= $n;
|
||||
}
|
||||
if (add_str($strings, $pre, $blacklist))
|
||||
{
|
||||
$pre =~ s/([\'\\])/\\$1/gso;
|
||||
$outtag .= ($outtag =~ /\%\]\s+$/ ? '[%+' : '[%')." L('$pre') %]";
|
||||
}
|
||||
else
|
||||
{
|
||||
$outtag .= $pre_raw;
|
||||
}
|
||||
$outtag .= $outtag2;
|
||||
last if $n eq '"' || $n eq "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($script)
|
||||
{
|
||||
if (lc $script eq 'script')
|
||||
{
|
||||
# Extract messages from TT directives inside <script>..</script>
|
||||
while ($template =~ s/^(.*?)(\[\%|<\/\s*script\s*>)//iso)
|
||||
{
|
||||
$outtag .= $1 . $2;
|
||||
if ($2 eq '[%')
|
||||
{
|
||||
$outtag .= tt_dir($template, $strings, $blacklist);
|
||||
}
|
||||
else
|
||||
{
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$template =~ s!.*?</$script\s*>!!is and $outtag .= $&;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($tag ne '')
|
||||
{
|
||||
$outtag = $tag;
|
||||
}
|
||||
$translated .= $pre_nl;
|
||||
if (add_str($strings, $pre, $blacklist))
|
||||
{
|
||||
$pre =~ s/([\'\\])/\\$1/gso;
|
||||
$translated .= ($translated =~ /\%\]\s+$/ ? '[%+' : '[%')." L('$pre') %]";
|
||||
if ($translated =~ /\%\](\s*)$/ && ($1 || $outtag =~ /^\s+\[\%/))
|
||||
{
|
||||
# Preserve space...
|
||||
$outtag =~ s/^(\s*)\[\%(?!\+|\s*\#)-?/$1\[\%\+/;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$translated .= $pre_raw;
|
||||
}
|
||||
$translated .= $outtag;
|
||||
last if $tag eq '';
|
||||
}
|
||||
$translated .= "\n";
|
||||
return ($translated, $strings);
|
||||
}
|
||||
|
||||
sub tt_dir
|
||||
{
|
||||
my (undef, $strings, $blacklist, $ignore) = @_;
|
||||
my $out = '';
|
||||
while ($_[0] =~ s/^\s*#(?:(?:[^\n\%]+|\%[^\]])*)//so || $_[0] =~ s/^(?:
|
||||
[^\"\'\%]+ |
|
||||
\%(?!\]) |
|
||||
\"((?:[^\"\\]+|\\.)*)\" |
|
||||
\'((?:[^\'\\]+|\\.)*)\')//xso)
|
||||
{
|
||||
my $m = $&;
|
||||
my $s = $1 || $2;
|
||||
$1 ? $s =~ s/\\([\\\"])/$1/gso : $s =~ s/\\([\\\'])/$1/gso;
|
||||
if ($s ne '')
|
||||
{
|
||||
while ($_[0] =~ s/^\s*_\s*(?:
|
||||
\"((?:[^\"\\]+|\\.)*)\" |
|
||||
\'((?:[^\'\\]+|\\.)*)\')//xso)
|
||||
{
|
||||
$m .= $&;
|
||||
my $a = $1 || $2;
|
||||
$1 ? $a =~ s/\\([\\\"])/$1/gso : $a =~ s/\\([\\\'])/$1/gso;
|
||||
$s .= $a;
|
||||
}
|
||||
}
|
||||
if (!$ignore && length $s > 2 && $s =~ /[^\x00-\x80]|[a-z].*[A-Z]|[A-Z].*[a-z]|\w.*\W|\W.*\w/so &&
|
||||
add_str($strings, $s, $blacklist))
|
||||
{
|
||||
$s =~ s/([\'\\])/\\$1/gso;
|
||||
$out .= "L('$s')";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($s)
|
||||
{
|
||||
#print STDERR "Skip: $s\n";
|
||||
}
|
||||
$out .= $m;
|
||||
}
|
||||
}
|
||||
$_[0] =~ s/\s*\%\]//so or die $_[0];
|
||||
$out .= "%]";
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub add_str
|
||||
{
|
||||
my ($strings, $s, $blacklist) = @_;
|
||||
my $chk = $s;
|
||||
$chk =~ s/<[^>]*>//gso;
|
||||
$chk =~ s/&([a-z]+|#x[0-9a-f]+|#\d+);|\$[a-z]+(\.[a-z_]+)?/ /giso;
|
||||
return 0 if $chk !~ /[^\W\d]|[^\x00-\x80]/ ||
|
||||
$chk =~ /^[a-z0-9_\-]*\.cgi(\?\S*)?$|^[a-z0-9_\-\/]*\.html(\.tmpl)?(#\S*)?$|^\/*skins.*\.css$|^\/*js.*\.js$/;
|
||||
$s =~ s/\n[ \t]+/\n/gso;
|
||||
return 0 if $blacklist->{$s};
|
||||
$_[1] = $s;
|
||||
if (!$strings->{$s})
|
||||
{
|
||||
$strings->{$s} = 1;
|
||||
push @{$strings->{''}}, $s;
|
||||
}
|
||||
return 1;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,72 @@
|
|||
// English messages for Bugzilla4Intranet JavaScripts
|
||||
// License: Dual-license GPL 3.0+ or MPL 1.1+
|
||||
// Author: Vitaliy Filippov <vitalif@mail.ru>
|
||||
|
||||
add_i18n({
|
||||
'Hide \u25B4':
|
||||
'Hide \u25B4',
|
||||
'Show \u25BE':
|
||||
'Show \u25BE',
|
||||
'Undo delete':
|
||||
'Undo delete',
|
||||
'Delete':
|
||||
'Delete',
|
||||
'<no groups>':
|
||||
'<no groups>',
|
||||
'You cannot paste images from clipboard because Java support is not enabled in your browser. Please download Java plugin from http://java.com/':
|
||||
'You cannot paste images from clipboard because Java support is not enabled in your browser. Please download Java plugin from http://java.com/',
|
||||
'You must enter a Description for this attachment.':
|
||||
'You must enter a Description for this attachment.',
|
||||
'Collapse the comment.':
|
||||
'Collapse the comment.',
|
||||
'Expand the comment.':
|
||||
'Expand the comment.',
|
||||
'Hide full text':
|
||||
'Hide full text',
|
||||
'Show full text':
|
||||
'Show full text',
|
||||
'reply':
|
||||
'reply',
|
||||
'other':
|
||||
'other',
|
||||
'same':
|
||||
'same',
|
||||
'ext':
|
||||
'ext',
|
||||
'int':
|
||||
'int',
|
||||
' product':
|
||||
' product',
|
||||
'(table removed)':
|
||||
'(table removed)',
|
||||
'Please, verify working time:':
|
||||
'Please, verify working time:',
|
||||
'Choose a component to see its description.':
|
||||
'Choose a component to see its description.',
|
||||
'Your comment will be lost. Leave page?':
|
||||
'Your comment will be lost. Leave page?',
|
||||
'Show Obsolete':
|
||||
'Show Obsolete',
|
||||
'Hide Obsolete':
|
||||
'Hide Obsolete',
|
||||
'Save All Changes':
|
||||
'Save All Changes',
|
||||
'Please enter a summary sentence for this bug.':
|
||||
'Please enter a summary sentence for this bug.',
|
||||
'No keywords found':
|
||||
'No keywords found',
|
||||
'Describe new keyword':
|
||||
'Describe new keyword',
|
||||
'You must set the login and password before logging in.':
|
||||
'You must set the login and password before logging in.',
|
||||
'Type at least 3 letters':
|
||||
'Type at least 3 letters',
|
||||
'No users found':
|
||||
'No users found',
|
||||
'January February March April May June July August September October November December':
|
||||
'January February March April May June July August September October November December',
|
||||
'Mon Tue Wed Thu Fri Sat Sun':
|
||||
'Mon Tue Wed Thu Fri Sat Sun',
|
||||
'Cancel':
|
||||
'Cancel'
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,330 @@
|
|||
{
|
||||
install_strings => {
|
||||
any => 'any',
|
||||
blacklisted => '(blacklisted)',
|
||||
checking_for => 'Checking for',
|
||||
checking_dbd => 'Checking available perl DBD modules...',
|
||||
checking_optional => 'The following Perl modules are optional:',
|
||||
checking_modules => 'Checking perl modules...',
|
||||
chmod_failed => '##path##: Failed to change permissions: ##error##',
|
||||
chown_failed => '##path##: Failed to change ownership: ##error##',
|
||||
commands_dbd => <<EOT,
|
||||
YOU MUST RUN ONE OF THE FOLLOWING COMMANDS (depending on which database
|
||||
you use):
|
||||
EOT
|
||||
commands_optional => 'COMMANDS TO INSTALL OPTIONAL MODULES:',
|
||||
commands_required => <<EOT,
|
||||
COMMANDS TO INSTALL REQUIRED MODULES (You *must* run all these commands
|
||||
and then re-run this script):
|
||||
EOT
|
||||
done => 'done.',
|
||||
extension_must_return_name => <<END,
|
||||
##file## returned ##returned##, which is not a valid name for an extension.
|
||||
Extensions must return their name, not <code>1</code> or a number. See
|
||||
the documentation of Bugzilla::Extension for details.
|
||||
END
|
||||
feature_auth_ldap => 'LDAP Authentication',
|
||||
feature_auth_radius => 'RADIUS Authentication',
|
||||
feature_graphical_reports => 'Graphical Reports',
|
||||
feature_inbound_email => 'Inbound Email',
|
||||
feature_jobqueue => 'Mail Queueing',
|
||||
feature_jsonrpc => 'JSON-RPC Interface',
|
||||
feature_new_charts => 'New Charts',
|
||||
feature_old_charts => 'Old Charts',
|
||||
feature_mod_perl => 'mod_perl',
|
||||
feature_moving => 'Move Bugs Between Installations',
|
||||
feature_patch_viewer => 'Patch Viewer',
|
||||
feature_rand_security => 'Improve cookie and token security',
|
||||
feature_smtp_auth => 'SMTP Authentication',
|
||||
feature_updates => 'Automatic Update Notifications',
|
||||
feature_xmlrpc => 'XML-RPC Interface',
|
||||
feature_fulltext_stem => 'Snowball stemmers in full-text search',
|
||||
|
||||
header => '* This is Bugzilla ##bz_ver## on perl ##perl_ver##
|
||||
* Running on ##os_name## ##os_ver##',
|
||||
install_all => <<EOT,
|
||||
|
||||
To attempt an automatic install of every required and optional module
|
||||
with one command, do:
|
||||
|
||||
##perl## install-module.pl --all
|
||||
|
||||
EOT
|
||||
install_data_too_long => <<EOT,
|
||||
WARNING: Some of the data in the ##table##.##column## column is longer than
|
||||
its new length limit of ##max_length## characters. The data that needs to be
|
||||
fixed is printed below with the value of the ##id_column## column first and
|
||||
then the value of the ##column## column that needs to be fixed:
|
||||
|
||||
EOT
|
||||
install_module => 'Installing ##module## version ##version##...',
|
||||
installation_failed => '*** Installation aborted. Read the messages above. ***',
|
||||
max_allowed_packet => <<EOT,
|
||||
WARNING: You need to set the max_allowed_packet parameter in your MySQL
|
||||
configuration to at least ##needed##. Currently it is set to ##current##.
|
||||
You can set this parameter in the [mysqld] section of your MySQL
|
||||
configuration file.
|
||||
EOT
|
||||
min_version_required => 'Minimum version required: ',
|
||||
modules_message_db => <<EOT,
|
||||
***********************************************************************
|
||||
* DATABASE ACCESS *
|
||||
***********************************************************************
|
||||
* In order to access your database, Bugzilla requires that the *
|
||||
* correct "DBD" module be installed for the database that you are *
|
||||
* running. See below for the correct command to run to install the *
|
||||
* appropriate module for your database. *
|
||||
EOT
|
||||
modules_message_optional => <<EOT,
|
||||
***********************************************************************
|
||||
* OPTIONAL MODULES *
|
||||
***********************************************************************
|
||||
* Certain Perl modules are not required by Bugzilla, but by *
|
||||
* installing the latest version you gain access to additional *
|
||||
* features. *
|
||||
* *
|
||||
* The optional modules you do not have installed are listed below, *
|
||||
* with the name of the feature they enable. Below that table are the *
|
||||
* commands to install each module. *
|
||||
EOT
|
||||
modules_message_required => <<EOT,
|
||||
***********************************************************************
|
||||
* REQUIRED MODULES *
|
||||
***********************************************************************
|
||||
* Bugzilla requires you to install some Perl modules which are either *
|
||||
* missing from your system, or the version on your system is too old. *
|
||||
* See below for commands to install these modules. *
|
||||
EOT
|
||||
module_found => 'found v##ver##',
|
||||
module_not_found => 'not found',
|
||||
module_ok => 'ok',
|
||||
module_unknown_version => 'found unknown version',
|
||||
no_such_module => 'There is no Perl module on CPAN named ##module##.',
|
||||
ppm_repo_add => <<EOT,
|
||||
***********************************************************************
|
||||
* Note For Windows Users *
|
||||
***********************************************************************
|
||||
* In order to install the modules listed below, you first have to run *
|
||||
* the following command as an Administrator: *
|
||||
* *
|
||||
* ppm repo add theory58S ##theory_url##
|
||||
EOT
|
||||
ppm_repo_up => <<EOT,
|
||||
* *
|
||||
* Then you have to do (also as an Administrator): *
|
||||
* *
|
||||
* ppm repo up theory58S *
|
||||
* *
|
||||
* Do that last command over and over until you see "theory58S" at the *
|
||||
* top of the displayed list. *
|
||||
EOT
|
||||
template_precompile => 'Precompiling templates...',
|
||||
template_removal_failed => <<END,
|
||||
WARNING: The directory '##datadir##/template' could not be removed.
|
||||
It has been moved into '##datadir##/deleteme', which should be
|
||||
deleted manually to conserve disk space.
|
||||
END
|
||||
template_removing_dir => 'Removing existing compiled templates...',
|
||||
},
|
||||
terms => {
|
||||
bug => 'bug',
|
||||
Bug => 'Bug',
|
||||
abug => 'a bug',
|
||||
Abug => 'A bug',
|
||||
aBug => 'a Bug',
|
||||
ABug => 'A Bug',
|
||||
bugs => 'bugs',
|
||||
Bugs => 'Bugs',
|
||||
bugmail => 'bugmail',
|
||||
Bugmail => 'Bugmail',
|
||||
zeroSearchResults => 'Zarro Boogs found',
|
||||
Bugzilla => 'Bugzilla',
|
||||
},
|
||||
operator_descs => {
|
||||
not => 'NOT',
|
||||
noop => '---',
|
||||
equals => 'is equal to',
|
||||
notequals => 'is not equal to',
|
||||
anyexact => 'is equal to any of the strings',
|
||||
substring => 'contains the string',
|
||||
notsubstring => 'does not contain the string',
|
||||
casesubstring => 'contains the string (exact case)',
|
||||
notcasesubstring => 'does not contain the string (exact case)',
|
||||
anywordssubstr => 'contains any of the strings',
|
||||
allwordssubstr => 'contains all of the strings',
|
||||
nowordssubstr => 'contains none of the strings',
|
||||
regexp => 'matches regular expression',
|
||||
notregexp => 'does not match regular expression',
|
||||
lessthan => 'is less than',
|
||||
lessthaneq => 'is less than or equal to',
|
||||
greaterthan => 'is greater than',
|
||||
greaterthaneq => 'is greater than or equal to',
|
||||
anywords => 'contains any of the words',
|
||||
allwords => 'contains all of the words',
|
||||
nowords => 'contains none of the words',
|
||||
matches => 'matches',
|
||||
notmatches => 'does not match',
|
||||
insearch => 'matched by saved search',
|
||||
notinsearch => 'not matched by saved search',
|
||||
changedbefore => 'changed before',
|
||||
changedafter => 'changed after',
|
||||
changedfrom => 'changed from',
|
||||
changedto => 'changed to',
|
||||
changedby => 'changed by',
|
||||
desc_between => 'between $1 and $2',
|
||||
desc_before => 'before $2',
|
||||
desc_after => 'after $1',
|
||||
desc_by => 'by $1',
|
||||
desc_fields => 'one of $1',
|
||||
},
|
||||
field_types => {
|
||||
UNKNOWN => 'Unknown Type',
|
||||
FREETEXT => 'Free Text',
|
||||
SINGLE_SELECT => 'Drop Down',
|
||||
MULTI_SELECT => 'Multiple-Selection Box',
|
||||
TEXTAREA => 'Large Text Box',
|
||||
DATETIME => 'Date/Time',
|
||||
BUG_ID => '$terms.Bug ID',
|
||||
BUG_URLS => '$terms.Bug URLs',
|
||||
KEYWORDS => '(Invalid type) Keywords',
|
||||
NUMERIC => 'Numeric',
|
||||
EXTURL => 'External URL',
|
||||
BUG_ID_REV => '$terms.Bug ID reverse',
|
||||
},
|
||||
field_descs => {
|
||||
alias => 'Alias',
|
||||
assigned_to => 'Assignee',
|
||||
blocked => 'Blocks',
|
||||
bug_file_loc => 'URL',
|
||||
bug_group => 'Group',
|
||||
bug_id => '$terms.Bug ID',
|
||||
bug_severity => 'Severity',
|
||||
bug_status => 'Status',
|
||||
cc => 'CC',
|
||||
classification => 'Classification',
|
||||
cclist_accessible => 'CC list accessible',
|
||||
component_id => 'Component ID',
|
||||
component => 'Component',
|
||||
content => 'Content',
|
||||
comment => 'Comment',
|
||||
changes => 'Changed',
|
||||
'[Bug creation]' => 'Creation date',
|
||||
opendate => 'Creation date',
|
||||
creation_ts => 'Creation date',
|
||||
deadline => 'Deadline',
|
||||
changeddate => 'Changed',
|
||||
delta_ts => 'Changed',
|
||||
dependson => 'Depends on',
|
||||
dup_id => 'Duplicate of',
|
||||
estimated_time => 'Orig. Est.',
|
||||
everconfirmed => 'Ever confirmed',
|
||||
keywords => 'Keywords',
|
||||
newcc => 'CC',
|
||||
op_sys => 'OS',
|
||||
owner_idle_time => 'Time Since Assignee Touched',
|
||||
days_elapsed => 'Days since bug changed',
|
||||
percentage_complete => '% Complete',
|
||||
priority => 'Priority',
|
||||
product_id => 'Product ID',
|
||||
product => 'Product',
|
||||
qa_contact => 'QA Contact',
|
||||
remaining_time => 'Hours Left',
|
||||
rep_platform => 'Hardware',
|
||||
reporter => 'Reporter',
|
||||
reporter_accessible => 'Reporter accessible',
|
||||
resolution => 'Resolution',
|
||||
see_also => 'See Also',
|
||||
setting => 'Setting',
|
||||
settings => 'Settings',
|
||||
short_desc => 'Summary',
|
||||
status_whiteboard => 'Whiteboard',
|
||||
target_milestone => 'Target Milestone',
|
||||
version => 'Version',
|
||||
votes => 'Votes',
|
||||
comment0 => 'First Comment',
|
||||
interval_time => 'Period Worktime',
|
||||
work_time => 'Hours Worked',
|
||||
actual_time => 'Hours Worked',
|
||||
longdesc => 'Comment',
|
||||
commenter => 'Commenter',
|
||||
'longdescs.isprivate' => 'Comment is private',
|
||||
'attachments.description' => 'Attachment description',
|
||||
'attachments.filename' => 'Attachment filename',
|
||||
'attachments.mimetype' => 'Attachment mime type',
|
||||
'attachments.ispatch' => 'Attachment is patch',
|
||||
'attachments.isobsolete' => 'Attachment is obsolete',
|
||||
'attachments.isprivate' => 'Attachment is private',
|
||||
'attachments.submitter' => 'Attachment creator',
|
||||
'flagtypes.name' => 'Flags and Requests',
|
||||
'requestees.login_name' => 'Flag Requestee',
|
||||
'setters.login_name' => 'Flag Setter',
|
||||
},
|
||||
setting_descs => {
|
||||
comment_sort_order => 'When viewing $terms.abug, show comments in this order',
|
||||
csv_colsepchar => 'Field separator character for CSV files',
|
||||
display_quips => 'Show a quip at the top of each $terms.bug list',
|
||||
zoom_textareas => 'Zoom textareas large when in use (requires JavaScript)',
|
||||
newest_to_oldest => 'Newest to Oldest',
|
||||
newest_to_oldest_desc_first => 'Newest to Oldest, but keep Description at the top',
|
||||
off => 'Off',
|
||||
oldest_to_newest => 'Oldest to Newest',
|
||||
on => 'On',
|
||||
post_bug_submit_action => 'After changing $terms.abug',
|
||||
next_bug => 'Show next $terms.bug in my list',
|
||||
same_bug => 'Show the updated $terms.bug',
|
||||
standard => 'Classic',
|
||||
skin => '$terms.Bugzilla\'s general appearance (skin)',
|
||||
nothing => 'Do Nothing',
|
||||
state_addselfcc => 'Automatically add me to the CC list of $terms.bugs I change',
|
||||
always => 'Always',
|
||||
never => 'Never',
|
||||
cc_unless_role => 'Only if I have no role on them',
|
||||
lang => 'Default language for UI and emails',
|
||||
quote_replies => 'Quote the associated comment when you click on its reply link',
|
||||
quoted_reply => 'Quote the full comment',
|
||||
simple_reply => 'Reference the comment number only',
|
||||
timezone => 'Timezone used to display dates and times',
|
||||
local => 'Same as the server',
|
||||
remind_me_about_worktime => 'Remind me to track worktime for each comment',
|
||||
remind_me_about_worktime_newbug => 'Remind me to track worktime for new bugs',
|
||||
remind_me_about_flags => 'Remind me about flag requests',
|
||||
saved_searches_position => 'Position of Saved Searches bar',
|
||||
footer => 'Page footer',
|
||||
header => 'Page header',
|
||||
both => 'Both',
|
||||
csv_charset => 'Character set for CSV import and export',
|
||||
silent_affects_flags => 'Do not send flag email under Silent mode',
|
||||
send => 'Send',
|
||||
do_not_send => 'Don\'t send',
|
||||
showhide_comments => 'Which comments can be marked as collapsed',
|
||||
none => 'None',
|
||||
worktime => 'With worktime only',
|
||||
all => 'All',
|
||||
comment_width => 'Show comments in the full screen width',
|
||||
preview_long_comments => 'Fold long comments',
|
||||
clear_requests_on_close => 'Clear flag requests when closing bugs',
|
||||
show_gravatars => 'Show avatar images (Gravatars)',
|
||||
},
|
||||
system_groups => {
|
||||
admin => 'Administrator group. Usually allows to access <b>all</b> administrative functions.',
|
||||
admin_index => 'Allows to <a href="admin.cgi">enter Administration area</a>, granted automatically if you can access any of the administrative functions.',
|
||||
tweakparams => 'Allows to <a href="editparams.cgi">change Parameters</a>.',
|
||||
editusers => 'Allows to <a href="editusers.cgi">edit or disable users</a> and include/exclude them from <b>all</b> groups.',
|
||||
creategroups => 'Allows to <a href="editgroups.cgi">create, destroy and edit groups</a>.',
|
||||
editclassifications => 'Allows to <a href="editclassifications.cgi">create, destroy and edit classifications</a>.',
|
||||
editcomponents => 'Allows to <a href="editproducts.cgi">create, destroy and edit all products, components, versions and milestones</a>.',
|
||||
editkeywords => 'Allows to <a href="editvalues.cgi?field=keywords">create, destroy and edit keywords</a>.',
|
||||
editbugs => 'Allows to edit all fields of all bugs.',
|
||||
canconfirm => 'Allows to confirm bugs or mark them as duplicates.',
|
||||
bz_canusewhineatothers => 'Allows to <a href="editwhines.cgi">configure whine reports for other users</a>.',
|
||||
bz_canusewhines => 'Allows to <a href="editwhines.cgi">configure whine reports for self</a>.',
|
||||
bz_sudoers => 'Allows to <a href="relogin.cgi?action=prepare-sudo">impersonate other users</a> and perform actions as them.',
|
||||
bz_sudo_protect => 'Forbids other users to impersonate you.',
|
||||
bz_editcheckers => 'Allows to <a href="editcheckers.cgi">edit Predicates</a> ("Correctness Checkers").',
|
||||
editfields => 'Allows to <a href="editfields.cgi">create, destroy and edit properties of bug fields</a>.',
|
||||
editvalues => 'Allows to <a href="editvalues.cgi">edit allowed values for all bug fields</a>.',
|
||||
importxls => 'Allows to <a href="importxls.cgi">create or update many bugs at once using Excel and CSV files</a>.',
|
||||
worktimeadmin => 'Allows to register working time for other users and for dates in the past (see "Fix Worktime" link under a bug list).',
|
||||
editflagtypes => 'Allows to <a href="editflagtypes.cgi">create, destroy and edit flag types</a>.',
|
||||
},
|
||||
};
|
|
@ -11,12 +11,12 @@ function helpToggle(btn_id, div_id)
|
|||
var d = document.getElementById(div_id);
|
||||
if (d.style.display == 'none')
|
||||
{
|
||||
b.value = 'Hide \u25B4';
|
||||
b.value = L('Hide \u25B4');
|
||||
d.style.display = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
b.value = 'Show \u25BE';
|
||||
b.value = L('Show \u25BE');
|
||||
d.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ function deleteGroup(el_link, grp_id)
|
|||
el_othercontrol.setAttribute('disabled', true);
|
||||
clearSelectedOption(el_membercontrol);
|
||||
clearSelectedOption(el_othercontrol);
|
||||
el_link.innerHTML = 'Undo delete';
|
||||
el_link.innerHTML = L('Undo delete');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -131,7 +131,7 @@ function deleteGroup(el_link, grp_id)
|
|||
el_othercontrol.removeAttribute('disabled');
|
||||
clearSelectedOption(el_membercontrol);
|
||||
clearSelectedOption(el_othercontrol);
|
||||
el_link.innerHTML = 'Delete';
|
||||
el_link.innerHTML = L('Delete');
|
||||
}
|
||||
highlightButton();
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ function addNewGroup()
|
|||
var cell_empty = row.insertCell(3);
|
||||
var cell_action = row.insertCell(4);
|
||||
|
||||
cell_action.innerHTML = '<a href="#" class="icon-delete" onclick="deleteGroup(this, ' + count_rows + '); return false;">Delete</a>';
|
||||
cell_action.innerHTML = '<a href="#" class="icon-delete" onclick="deleteGroup(this, ' + count_rows + '); return false;">'+L('Delete')+'</a>';
|
||||
var etalon_group = document.getElementById('etalon_groups');
|
||||
var new_group = document.createElement('select');
|
||||
new_group.id = 'group_' + count_rows;
|
||||
|
@ -191,7 +191,7 @@ function deleteGroupCheckbox(el_id)
|
|||
added_li = document.createElement('li');
|
||||
added_li.id = 'li_' + params_arr[0] + '_empty';
|
||||
added_li.className = 'group_empty';
|
||||
added_li.innerHTML = '<no groups>';
|
||||
added_li.innerHTML = L('<no groups>');
|
||||
exsist_list.appendChild(added_li);
|
||||
}
|
||||
highlightButton();
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
* Marc Schumann <wurblzap@gmail.com>
|
||||
*/
|
||||
|
||||
var SUPA_JAVA_DISABLED_ERROR = 'You cannot paste images from clipboard because Java support'+
|
||||
var SUPA_JAVA_DISABLED_ERROR = L('You cannot paste images from clipboard because Java support'+
|
||||
' is not enabled in your browser. Please download Java plugin'+
|
||||
' from http://java.com/';
|
||||
' from http://java.com/');
|
||||
|
||||
function htmlspecialchars_decode(str)
|
||||
{
|
||||
|
@ -121,7 +121,7 @@ function validateAttachmentForm(theform)
|
|||
var desc = theform.description.value.replace(/^\s+|\s+$/, '');
|
||||
if (desc == '')
|
||||
{
|
||||
alert(BUGZILLA.string.attach_desc_required);
|
||||
alert(L('You must enter a Description for this attachment.'));
|
||||
return false;
|
||||
}
|
||||
if (!encodeSupaContent())
|
||||
|
|
30
js/bug.js
30
js/bug.js
|
@ -35,7 +35,7 @@ function showhide_comment(comment_id, show)
|
|||
var link = document.getElementById('comment_link_' + comment_id);
|
||||
var comment = document.getElementById('comment_text_' + comment_id);
|
||||
link.innerHTML = show ? "[-]" : "[+]";
|
||||
link.title = (show ? "Collapse" : "Expand")+" the comment.";
|
||||
link.title = L(show ? "Collapse the comment." : "Expand the comment.");
|
||||
if (show)
|
||||
removeClass(comment, 'collapsed');
|
||||
else
|
||||
|
@ -60,7 +60,7 @@ function showhide_comment_preview(comment_id)
|
|||
body.style.display = 'block';
|
||||
addClass(link, "shown");
|
||||
}
|
||||
link.innerHTML = (!show ? "Hide" : "Show") + " full text";
|
||||
link.innerHTML = L(!show ? "Hide full text" : "Show full text");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ function addCollapseLink(id)
|
|||
' <a href="#" class="bz_collapse_comment"'+
|
||||
' id="comment_link_' + id +
|
||||
'" onclick="toggle_comment_display(' + id +
|
||||
'); return false;" title="'+(c ? 'Collapse' : 'Expand')+' the comment.">['+
|
||||
'); return false;" title="'+L(c ? 'Collapse the comment.' : 'Expand the comment.')+'">['+
|
||||
(c ? '-' : '+')+']<\/a> ';
|
||||
}
|
||||
|
||||
|
@ -100,16 +100,16 @@ function addReplyLink(num, id)
|
|||
if (user_settings.quote_replies != 'off')
|
||||
{
|
||||
s += '<a href="#add_comment" onclick="replyToComment(' +
|
||||
num + ', ' + id + '); return false;">reply<' + '/a>';
|
||||
num + ', ' + id + '); return false;">'+L('reply')+'<' + '/a>';
|
||||
}
|
||||
s += ', clone to <a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&cloned_comment='+num+'">other</a>';
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.product)+'&cloned_comment='+num+'">same</a>';
|
||||
s += ', clone to <a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&cloned_comment='+num+'">'+L('other')+'</a>';
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.product)+'&cloned_comment='+num+'">'+L('same')+'</a>';
|
||||
// 4Intranet Bug 69514 - Clone to external product button
|
||||
if (bug_info.extprod)
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.extprod)+'&cloned_comment='+num+'">ext</a>';
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.extprod)+'&cloned_comment='+num+'">'+L('ext')+'</a>';
|
||||
else if (bug_info.intprod)
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.intprod)+'&cloned_comment='+num+'">int</a>';
|
||||
s += ' product]';
|
||||
s += '/<a href="enter_bug.cgi?cloned_bug_id='+bug_info.id+'&product='+encodeURIComponent(bug_info.intprod)+'&cloned_comment='+num+'">'+L('int')+'</a>';
|
||||
s += L(' product')+']';
|
||||
e.innerHTML += s;
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ function getText(element)
|
|||
/* Adds the reply text to the `comment' textarea */
|
||||
function replyToComment(num, id)
|
||||
{
|
||||
var prefix = "(In reply to comment #" + num + ")\n";
|
||||
var prefix = "(In reply to comment #"+num+")\n";
|
||||
var replytext = "";
|
||||
if (user_settings.quote_replies == 'quoted_reply')
|
||||
{
|
||||
|
@ -204,7 +204,7 @@ function replyToComment(num, id)
|
|||
replytext += "\n";
|
||||
}
|
||||
else if (!prev_ist)
|
||||
replytext += "> (table removed)\n";
|
||||
replytext += "> "+L("(table removed)")+"\n";
|
||||
prev_ist = ist;
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ function changeform_onsubmit()
|
|||
(wt === null || wt === undefined || wt != wt ||
|
||||
notimetracking && wt != 0 || !notimetracking && wt == 0))
|
||||
{
|
||||
awt = prompt("Please, verify working time:", !wt || wt != wt ? "0" : wt);
|
||||
awt = prompt(L("Please, verify working time:"), !wt || wt != wt ? "0" : wt);
|
||||
if (awt === null || awt === undefined || (""+awt).length <= 0)
|
||||
{
|
||||
wtInput.focus();
|
||||
|
@ -389,7 +389,7 @@ addListener(window, 'beforeunload', function(e)
|
|||
if (window.checkCommentOnUnload && ta && ta.value.trim() != '')
|
||||
{
|
||||
e = e || window.event;
|
||||
return (e.returnValue = 'Your comment will be lost. Leave page?');
|
||||
return (e.returnValue = L('Your comment will be lost. Leave page?'));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -455,7 +455,7 @@ function showEditComment(comment_id)
|
|||
but_wrapper.className = 'edit_comment_submit';
|
||||
var submit_but = document.createElement('input');
|
||||
submit_but.type = 'submit';
|
||||
submit_but.value = 'Save All Changes';
|
||||
submit_but.value = L('Save All Changes');
|
||||
but_wrapper.appendChild(submit_but);
|
||||
parent.appendChild(but_wrapper);
|
||||
showhide_comment(key, false);
|
||||
|
@ -478,7 +478,7 @@ function toggle_obsolete_attachments(link)
|
|||
for (var i = 0; i < rs.length; i++)
|
||||
if (hasClass(rs[i], 'bz_tr_obsolete'))
|
||||
r0 = toggleClass(rs[i], 'bz_default_hidden');
|
||||
link.innerHTML = r0 ? 'Show Obsolete' : 'Hide Obsolete';
|
||||
link.innerHTML = r0 ? L('Show Obsolete') : L('Hide Obsolete');
|
||||
|
||||
var newHeight = table.offsetHeight;
|
||||
// This scrolling makes the window appear to not move at all.
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
* </script>
|
||||
*/
|
||||
var Calendar = {
|
||||
month_names: ["January","February","March","April","May","June","July","August","September","October","November","December"],
|
||||
weekdays: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],
|
||||
month_names: L("January February March April May June July August September October November December").split(' '),
|
||||
weekdays: L("Mon Tue Wed Thu Fri Sat Sun").split(' '),
|
||||
sunday: 6,
|
||||
month_days: [31,28,31,30,31,30,31,31,30,31,30,31],
|
||||
cancel_text: L('Cancel'),
|
||||
//Get today's date - year, month, day and date
|
||||
today : new Date(),
|
||||
opt : {},
|
||||
|
@ -144,7 +145,7 @@ var Calendar = {
|
|||
this.wrt("</tr>");
|
||||
}
|
||||
this.wrt("</table>");
|
||||
this.wrt("<input type='button' value='Cancel' class='calendar-cancel' onclick='Calendar.hideCalendar();' />");
|
||||
this.wrt("<input type='button' value='"+this.cancel_text+"' class='calendar-cancel' onclick='Calendar.hideCalendar();' />");
|
||||
|
||||
document.getElementById(this.opt['calendar']).innerHTML = this.data.join("");
|
||||
this.data = [];
|
||||
|
|
|
@ -90,7 +90,7 @@ function component_change()
|
|||
}
|
||||
else
|
||||
{
|
||||
document.getElementById('comp_desc').innerHTML = 'Choose a component to see its description.';
|
||||
document.getElementById('comp_desc').innerHTML = L('Choose a component to see its description.');
|
||||
document.getElementById('comp_desc').style.color = 'red';
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ function validateEntryForm(theform)
|
|||
|
||||
if (theform.short_desc.value == '')
|
||||
{
|
||||
alert('Please enter a summary sentence for this bug.');
|
||||
alert(L('Please enter a summary sentence for this bug.'));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ function validateEntryForm(theform)
|
|||
wt = 0;
|
||||
if (wantsReminder && (wt === null || noTimeTracking == (wt != 0)))
|
||||
{
|
||||
wt = prompt("Please, verify working time:", "0");
|
||||
wt = prompt(L("Please, verify working time:"), "0");
|
||||
if (wt == null || wt == undefined || (""+wt).length <= 0)
|
||||
{
|
||||
theform.work_time.focus();
|
||||
|
|
|
@ -382,7 +382,7 @@ function keywordAutocomplete(hint)
|
|||
function addKeywordsAutocomplete()
|
||||
{
|
||||
new SimpleAutocomplete("keywords", keywordAutocomplete,
|
||||
{ emptyText: 'No keywords found', multipleDelimiter: "," });
|
||||
{ emptyText: L('No keywords found'), multipleDelimiter: "," });
|
||||
}
|
||||
|
||||
// CustIS bug 66910 - check new keywords and requery description for it
|
||||
|
@ -421,7 +421,7 @@ function check_new_keywords(form)
|
|||
{
|
||||
this_value = document.getElementById('kd_' + i).value;
|
||||
}
|
||||
desc_html += "<div style='margin-top: 8px'><label>Describe new keyword <b>" + htmlspecialchars(non_exist_keywords[i]) +
|
||||
desc_html += "<div style='margin-top: 8px'><label>"+L("Describe new keyword")+" <b>" + htmlspecialchars(non_exist_keywords[i]) +
|
||||
"</b>:</label><br /><input type=\"text\" value=\"" + htmlspecialchars(this_value) + "\" class=\"text_input\" name=\"kd\" id=\"kd_" +
|
||||
i + "\" data-key=\"" + htmlspecialchars(non_exist_keywords[i]) + "\" style=\"border: solid 1px red;\" /></div>";
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ function check_mini_login_fields(suffix)
|
|||
if (mini_login.value != "" && mini_password.value != "" &&
|
||||
mini_login.value != mini_login_constants.login)
|
||||
return true;
|
||||
window.alert(mini_login_constants.warning);
|
||||
window.alert(L("You must set the login and password before logging in."));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ function set_language(value)
|
|||
{
|
||||
setCookie('LANG', value, {
|
||||
expires: new Date('January 1, 2038'),
|
||||
path: BUGZILLA.param.cookie_path
|
||||
path: BUGZILLA.param.cookiepath
|
||||
});
|
||||
window.location.reload();
|
||||
}
|
||||
|
@ -166,9 +166,9 @@ function userAutocomplete(hint, emptyOptions, loadAllOnEmpty)
|
|||
var data = convertUserList(r.users);
|
||||
// FIXME "3" constant, messages: remove hardcode, also in Bugzilla::User::match()
|
||||
if (data.length == 0 && hint.input.value.length < 3)
|
||||
hint.emptyText = 'Type at least 3 letters';
|
||||
hint.emptyText = L('Type at least 3 letters');
|
||||
else
|
||||
hint.emptyText = 'No users found';
|
||||
hint.emptyText = L('No users found');
|
||||
hint.replaceItems(data);
|
||||
}
|
||||
});
|
||||
|
|
22
js/util.js
22
js/util.js
|
@ -4,6 +4,28 @@
|
|||
* Contributor(s): Vitaliy Filippov <vitalif@mail.ru>
|
||||
*/
|
||||
|
||||
// Add i18n messages
|
||||
function add_i18n(messages)
|
||||
{
|
||||
if (!BUGZILLA.i18n)
|
||||
{
|
||||
BUGZILLA.i18n = messages;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var k in messages)
|
||||
{
|
||||
BUGZILLA.i18n[k] = messages[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get i18n string
|
||||
function L(str)
|
||||
{
|
||||
return BUGZILLA.i18n && BUGZILLA.i18n[str] || str;
|
||||
}
|
||||
|
||||
// Get the position of 'obj' from the page top
|
||||
// Returns [x, y], in pixels
|
||||
function findPos(obj)
|
||||
|
|
|
@ -183,7 +183,6 @@ $vars->{report_columns} = [
|
|||
];
|
||||
|
||||
# Boolean charts
|
||||
my $opdescs = Bugzilla->messages->{operator_descs};
|
||||
$vars->{chart_types} = Bugzilla::Search->CHART_OPERATORS_ORDER;
|
||||
$vars->{text_types} = Bugzilla::Search->TEXT_OPERATORS_ORDER;
|
||||
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
<!--
|
||||
mini_login_constants = {
|
||||
"login" : "login",
|
||||
"warning" : "You must set the login and password before logging in."
|
||||
};
|
||||
onDomReady(function() {
|
||||
init_mini_login_form('_[% qs_suffix %]');
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
[% IF Param('rememberlogin') == 'defaulton' ||
|
||||
[% IF Param('rememberlogin') == 'defaulton' ||
|
||||
Param('rememberlogin') == 'defaultoff' %]
|
||||
<tr>
|
||||
<th> </th>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% emailaddress %]
|
||||
Subject: [% terms.Bugzilla %] Change Email Address Request
|
||||
Subject: [% "$terms.Bugzilla Change Email Address Request" %]
|
||||
X-Bugzilla-Type: admin
|
||||
|
||||
[%+ terms.Bugzilla %] has received a request to change the email address
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% emailaddress %]
|
||||
Subject: [% terms.Bugzilla %] Change Email Address Request
|
||||
Subject: [% "$terms.Bugzilla Change Email Address Request" %]
|
||||
Importance: High
|
||||
X-MSMail-Priority: High
|
||||
X-Priority: 1
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% email %]
|
||||
Subject: [% terms.Bugzilla %]: confirm account creation
|
||||
Subject: [% "$terms.Bugzilla: confirm account creation" %]
|
||||
X-Bugzilla-Type: admin
|
||||
|
||||
[%+ terms.Bugzilla %] has received a request to create a user account
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% emailaddress %]
|
||||
Subject: [% terms.Bugzilla %] Change Password Request
|
||||
Subject: [% "$terms.Bugzilla Change Password Request" %]
|
||||
X-Bugzilla-Type: admin
|
||||
|
||||
You have (or someone impersonating you has) requested to change your
|
||||
|
|
|
@ -58,8 +58,8 @@ function SetCheckboxes(setting) {
|
|||
}
|
||||
}
|
||||
|
||||
document.write('<input type="button" value="Enable All Mail" onclick="SetCheckboxes(true); return false;" />\n');
|
||||
document.write('<input type="button" value="Disable All Mail" onclick="SetCheckboxes(false); return false;" />\n');
|
||||
document.write('<input type="button" value="[% L('Enable All Mail') %]" onclick="SetCheckboxes(true); return false;" />\n');
|
||||
document.write('<input type="button" value="[% L('Disable All Mail') %]" onclick="SetCheckboxes(false); return false;" />\n');
|
||||
// -->
|
||||
</script>
|
||||
|
||||
|
@ -304,10 +304,10 @@ preferences for <u>their</u> relationship to the [% terms.bug %]
|
|||
[%-# FIXME: remove hardcoded i18n message, also from js/field.js::userAutocomplete() %]
|
||||
new SimpleAutocomplete("new_watchedusers",
|
||||
function(h) { userAutocomplete(h, null); },
|
||||
{ multipleDelimiter: ',', emptyText: 'No users found' });
|
||||
{ multipleDelimiter: ',', emptyText: '[% L('No users found') %]' });
|
||||
new SimpleAutocomplete("new_watchers",
|
||||
function(h) { userAutocomplete(h, null); },
|
||||
{ multipleDelimiter: ',', emptyText: 'No users found' });
|
||||
{ multipleDelimiter: ',', emptyText: '[% L('No users found') %]' });
|
||||
//-->
|
||||
</script>
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td valign="top">Type:</td>
|
||||
<td valign="top">[% field_types.${field.type} | html %]</td>
|
||||
<td valign="top">[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.${field.type}} | html %]</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
|
|
@ -25,7 +25,15 @@
|
|||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% javascript = BLOCK %]
|
||||
[% PROCESS
|
||||
global/header.html.tmpl
|
||||
title = title
|
||||
doc_section = "custom-fields.html#" _ (field.id ? "edit" : "add") _ "-custom-fields"
|
||||
javascript_urls = [ "js/cf-edit.js" ]
|
||||
%]
|
||||
|
||||
[%# FIXME Move to "js resource data" %]
|
||||
<script language="JavaScript">
|
||||
var constants = {
|
||||
FIELD_TYPE_SINGLE_SELECT: [% constants.FIELD_TYPE_SINGLE_SELECT %],
|
||||
FIELD_TYPE_MULTI_SELECT: [% constants.FIELD_TYPE_MULTI_SELECT %],
|
||||
|
@ -33,14 +41,7 @@ var constants = {
|
|||
FIELD_TYPE_EXTURL: [% constants.FIELD_TYPE_EXTURL %],
|
||||
FIELD_TYPE_BUG_ID_REV: [% constants.FIELD_TYPE_BUG_ID_REV %]
|
||||
};
|
||||
[% END %]
|
||||
|
||||
[% PROCESS
|
||||
global/header.html.tmpl
|
||||
title = title
|
||||
doc_section = "custom-fields.html#" _ (field.id ? "edit" : "add") _ "-custom-fields"
|
||||
javascript_urls = [ "js/cf-edit.js" ]
|
||||
%]
|
||||
</script>
|
||||
|
||||
[% IF !field.id %]
|
||||
<p style="font-size: 120%">
|
||||
|
@ -123,15 +124,17 @@ var constants = {
|
|||
<th align="left">Type:</th>
|
||||
<td valign="top">
|
||||
[% IF field.id %]
|
||||
[% field_types.${field.type} | html %]
|
||||
[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.${field.type}} | html %]
|
||||
[% ELSE %]
|
||||
<select id="type" name="type" onchange="onChangeType()">
|
||||
[% FOREACH type = field_types.sort %]
|
||||
[% FOREACH type = constants.FIELD_TYPE_NAMES.sort %]
|
||||
[%# Types "Bug URLs" and "Keywords" are rudiments from original Bugzilla %]
|
||||
[% NEXT IF type == constants.FIELD_TYPE_UNKNOWN ||
|
||||
type == constants.FIELD_TYPE_BUG_URLS ||
|
||||
type == constants.FIELD_TYPE_KEYWORDS %]
|
||||
<option value="[% type | html %]">[% field_types.$type | html %]</option>
|
||||
<option value="[% type | html %]">
|
||||
[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.$type} | html %]
|
||||
</option>
|
||||
[% END %]
|
||||
</select>
|
||||
[% END %]
|
||||
|
@ -172,7 +175,7 @@ var constants = {
|
|||
[% IF !field.id || field.can_tweak('mailhead') %]
|
||||
<tr>
|
||||
<th align="left">
|
||||
<label for="mailhead">Displayed in [% terms.bug %]mail for new [% terms.bugs %]:</label>
|
||||
<label for="mailhead">Displayed in [% terms.bugmail %] for new [% terms.bugs %]:</label>
|
||||
</th>
|
||||
<td><input type="checkbox" id="mailhead" name="mailhead" value="1" [%- ' checked="checked"' IF field.in_new_bugmail %] /></td>
|
||||
</tr>
|
||||
|
|
|
@ -44,8 +44,8 @@
|
|||
},
|
||||
{
|
||||
name => "mailhead"
|
||||
heading => "${terms.Bug}mail",
|
||||
hint => "Included in ${terms.bug}mail for new $terms.bugs"
|
||||
heading => terms.Bugmail,
|
||||
hint => "Included in $terms.bugmail for new $terms.bugs"
|
||||
content_values => { '0' => '', '1' => 'Yes' }
|
||||
align => 'center'
|
||||
},
|
||||
|
@ -76,10 +76,10 @@
|
|||
[%# We want to display the type name of fields, not their type ID. %]
|
||||
[% overrides.type = {} %]
|
||||
|
||||
[% FOREACH field_type = field_types.keys %]
|
||||
[% FOREACH field_type = constants.FIELD_TYPE_NAMES.keys %]
|
||||
[% overrides.type.type.$field_type = {
|
||||
override_content => 1
|
||||
content => field_types.$field_type
|
||||
content => lc_messages.field_types.${constants.FIELD_TYPE_NAMES.$field_type}
|
||||
}
|
||||
%]
|
||||
[% END %]
|
||||
|
|
|
@ -194,7 +194,7 @@ var except_field_index = 0;
|
|||
function showhide_allowdeny()
|
||||
{
|
||||
var chk = document.getElementById('deny_all').checked;
|
||||
document.getElementById('except_fields_title').innerHTML = chk ? 'Но разрешать:' : '';
|
||||
document.getElementById('except_fields_title').innerHTML = chk ? '[% L('Но разрешать:') %]' : '';
|
||||
document.getElementById('except_fields_tr').style.backgroundColor = chk ? '#E0FFE0' : '#FFE0E0';
|
||||
}
|
||||
function add_field(fld, val)
|
||||
|
@ -225,7 +225,7 @@ function check_trigger()
|
|||
cc.value = fl.value = '';
|
||||
else if (!cc.value && !fl.value)
|
||||
{
|
||||
alert('Задайте действие триггера!');
|
||||
alert('[% L('Задайте действие триггера!') %]');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
#%]
|
||||
|
||||
[% title = BLOCK %]
|
||||
Delete Value '[% value.name | html %]' from the
|
||||
'[% field.description | html %]' ([% field.name | html %]) field
|
||||
[% L('Delete Value "$1" from the field "$2" ($3)', html(value.name), html(field.description), html(field.name)) %]
|
||||
[% END %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Author(s): Vitaliy Filippov <vitalif@mail.ru>, Stas Fomin <stas-fomin@yandex.ru> %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
title = "Add/remove users in group: $group.name"
|
||||
title = "Add/remove users in group: " _ group.name
|
||||
%]
|
||||
|
||||
<form method="POST" action="?">
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
_ "<p>For added security, you can insert <tt>%bugid%</tt> into the URL,"
|
||||
_ " which will be replaced with the ID of the current $terms.bug that"
|
||||
_ " the attachment is on, when you access an attachment. This will limit"
|
||||
_ " attachments to accessing only other attachments on the same"
|
||||
_ " ${terms.bug}. Remember, though, that all those possible domain names "
|
||||
_ " attachments to accessing only other attachments on the same $terms.bug"
|
||||
_ ". Remember, though, that all those possible domain names "
|
||||
_ " (such as <tt>1234.your.domain.com</tt>) must point to this same"
|
||||
_ " $terms.Bugzilla instance.",
|
||||
|
||||
|
@ -77,7 +77,7 @@
|
|||
"The maximum size (in kilobytes) of attachments <b>stored in the database</b>. " _
|
||||
"$terms.Bugzilla will not accept attachments greater than this number " _
|
||||
"of kilobytes in size. Setting this parameter to 0 will prevent " _
|
||||
"attaching files to ${terms.bugs}.",
|
||||
"attaching files to $terms.bugs" _ ".",
|
||||
|
||||
force_attach_bigfile =>
|
||||
"If this option is on, all attachments will be stored as local files, not inside the database.",
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
<dl>
|
||||
<dt>DB</dt>
|
||||
<dd>
|
||||
${terms.Bugzilla}'s built-in authentication. This is the most common
|
||||
$terms.Bugzilla's built-in authentication. This is the most common
|
||||
choice.
|
||||
</dd>
|
||||
<dt>RADIUS</dt>
|
||||
|
|
|
@ -49,19 +49,19 @@
|
|||
|
||||
letsubmitterchoosepriority =>
|
||||
"If this is on, then people submitting $terms.bugs can " _
|
||||
"choose an initial priority for that ${terms.bug}. " _
|
||||
"choose an initial priority for that $terms.bug" _ ". " _
|
||||
"If off, then all $terms.bugs initially have the default " _
|
||||
"priority selected below."
|
||||
|
||||
letsubmitterchoosemilestone =>
|
||||
"If this is on, then people submitting $terms.bugs can " _
|
||||
"choose the Target Milestone for that ${terms.bug}. " _
|
||||
"choose the Target Milestone for that $terms.bug" _ ". " _
|
||||
"If off, then all $terms.bugs initially have the default " _
|
||||
"milestone for the product being filed in."
|
||||
|
||||
musthavemilestoneonaccept =>
|
||||
"If you are using Target Milestone, do you want to require that " _
|
||||
"the milestone be set in order for a user to ACCEPT a ${terms.bug}?"
|
||||
"the milestone be set in order for a user to ACCEPT a $terms.bug?"
|
||||
|
||||
commentonchange_resolution =>
|
||||
"If this option is on, the user needs to enter a short " _
|
||||
|
|
|
@ -35,14 +35,14 @@
|
|||
|
||||
"move-to-url" => "The URL of the database we allow some of our $terms.bugs to be moved to.",
|
||||
|
||||
"move-to-address" => "To move ${terms.bugs}, an email is sent to the target database. This is " _
|
||||
"the email address that database uses to listen for incoming ${terms.bugs}.",
|
||||
"move-to-address" => "To move $terms.bugs, an email is sent to the target database. This is " _
|
||||
"the email address that database uses to listen for incoming $terms.bugs" _ ".",
|
||||
|
||||
"moved-from-address" => "To move ${terms.bugs}, an email is sent to the target database. This is " _
|
||||
"moved-from-address" => "To move $terms.bugs, an email is sent to the target database. This is " _
|
||||
"the email address from which this mail, and error messages are sent.",
|
||||
|
||||
movers => "A list of people with permission to move $terms.bugs and reopen moved " _
|
||||
"${terms.bugs} (in case the move operation fails).",
|
||||
"$terms.bugs (in case the move operation fails).",
|
||||
|
||||
"moved-default-product" => "$terms.Bugs moved from other databases to here are assigned " _
|
||||
"to this product.",
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
%]
|
||||
|
||||
[% param_descs = {
|
||||
webdotbase => "It is possible to show graphs of dependent ${terms.bugs}. You may set
|
||||
webdotbase => "It is possible to show graphs of dependent $terms.bugs" _ ". You may set
|
||||
this parameter to any of the following:
|
||||
<ul>
|
||||
<li>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
[% param_descs = {
|
||||
maintainer =>
|
||||
"The email address of the person who maintains this installation "
|
||||
_ " of ${terms.Bugzilla}. The address need not be that of a valid Bugzilla account.",
|
||||
_ " of $terms.Bugzilla" _ ". The address need not be that of a valid Bugzilla account.",
|
||||
|
||||
error_log =>
|
||||
"Path to Bugzilla error log file or empty string if you don't want to write an error log. " _
|
||||
|
@ -52,7 +52,7 @@
|
|||
_ " documentation is available in that language).",
|
||||
|
||||
utf8 =>
|
||||
"Use UTF-8 (Unicode) encoding for all text in ${terms.Bugzilla}. New"
|
||||
"Use UTF-8 (Unicode) encoding for all text in $terms.Bugzilla" _ ". New"
|
||||
_ " installations should set this to true to avoid character encoding"
|
||||
_ " problems. <strong>Existing databases should set this to true"
|
||||
_ " only after the data has been converted from existing legacy"
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<hr />
|
||||
|
||||
[%# Strictly speaking, we should not have to check for a
|
||||
classification if they are enabled, but I'm just being paranoid %]
|
||||
# classification if they are enabled, but I'm just being paranoid %]
|
||||
[% UNLESS no_add_product_link || !user.in_group("editcomponents") %]
|
||||
[% IF Bugzilla.get_field('classification').enabled && classification %]
|
||||
<p>
|
||||
|
|
|
@ -22,6 +22,13 @@
|
|||
constants.CONTROLMAPMANDATORY,
|
||||
] %]
|
||||
|
||||
[% control_names = {};
|
||||
control_names.${constants.CONTROLMAPNA} = 'NA';
|
||||
control_names.${constants.CONTROLMAPSHOWN} = 'Shown';
|
||||
control_names.${constants.CONTROLMAPDEFAULT} = 'Default';
|
||||
control_names.${constants.CONTROLMAPMANDATORY} = 'Mandatory';
|
||||
%]
|
||||
|
||||
[% all_groups = product.group_controls_full_data.values.sort("name") %]
|
||||
[% groups = product.group_controls.values.sort("name") %]
|
||||
|
||||
|
@ -36,7 +43,7 @@
|
|||
[% BLOCK control_select %]
|
||||
<select name="[% id %]" id="[% id %]" data-lastvalue="0">
|
||||
[% FOR i = control_options %]
|
||||
<option value="[% i %]" [% " selected=\"selected\"" IF control == i %]>[% lc_messages.control_options.$i %]</option>
|
||||
<option value="[% i %]" [% " selected=\"selected\"" IF control == i %]>[% control_names.$i %]</option>
|
||||
[% END %]
|
||||
</select>
|
||||
[% END %]
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
[% END %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
title = "Select product $classification_title"
|
||||
title = "Select product: " _ classification_title
|
||||
%]
|
||||
|
||||
[% edit_contentlink = BLOCK %]
|
||||
|
|
|
@ -70,11 +70,11 @@
|
|||
</tr>
|
||||
[% END %]
|
||||
<tr>
|
||||
<th><label for="disable_mail">[% terms.Bug %]mail Disabled:</label></th>
|
||||
<th><label for="disable_mail">[% terms.Bugmail %] Disabled:</label></th>
|
||||
<td>
|
||||
<input type="checkbox" name="disable_mail" id="disable_mail" value="1"
|
||||
[% IF otheruser.email_disabled %] checked="checked" [% END %] />
|
||||
(This affects [% terms.bug %]mail and whinemail, not password-reset or other
|
||||
(This affects [% terms.bug %], flag and whine emails, not password-reset or other
|
||||
non-[% terms.bug %]-related emails)
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
</font>
|
||||
<p style="margin-bottom: 0">
|
||||
You can either <a href="[% urlbase FILTER html %]attachment.cgi?bugid=[% bugid FILTER url_quote %]&action=enter">
|
||||
create a new attachment</a> or [% "go back to $terms.bug $bugid" FILTER bug_link(bugid) FILTER none %].
|
||||
create a new attachment</a> or [% "go back to $terms.bug " _ bugid FILTER bug_link(bugid) FILTER none %].
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -222,13 +222,13 @@
|
|||
var patchviewerinstalled = 1;
|
||||
document.write('<iframe id="viewDiffFrame" style="height: 400px; width: 100%; display: none;"><\/iframe>');
|
||||
[% END %]
|
||||
document.write('<button type="button" id="editButton" onclick="editAsComment(patchviewerinstalled);">Edit Attachment As Comment<\/button>');
|
||||
document.write('<button type="button" id="undoEditButton" onclick="undoEditAsComment(patchviewerinstalled);" style="display: none;">Undo Edit As Comment<\/button>');
|
||||
document.write('<button type="button" id="redoEditButton" onclick="redoEditAsComment(patchviewerinstalled);" style="display: none;">Redo Edit As Comment<\/button>');
|
||||
document.write('<button type="button" id="editButton" onclick="editAsComment(patchviewerinstalled);">[% L('Edit Attachment As Comment') %]<\/button>');
|
||||
document.write('<button type="button" id="undoEditButton" onclick="undoEditAsComment(patchviewerinstalled);" style="display: none;">[% L('Undo Edit As Comment') %]<\/button>');
|
||||
document.write('<button type="button" id="redoEditButton" onclick="redoEditAsComment(patchviewerinstalled);" style="display: none;">[% L('Redo Edit As Comment') %]<\/button>');
|
||||
[% IF use_patchviewer %]
|
||||
document.write('<button type="button" id="viewDiffButton" onclick="viewDiff(attachment_id, patchviewerinstalled);">View Attachment As Diff<\/button>');
|
||||
document.write('<button type="button" id="viewDiffButton" onclick="viewDiff(attachment_id, patchviewerinstalled);">[% L('View Attachment As Diff') %]<\/button>');
|
||||
[% END %]
|
||||
document.write('<button type="button" id="viewRawButton" onclick="viewRaw(patchviewerinstalled);" style="display: none;">View Attachment As Raw<\/button>');
|
||||
document.write('<button type="button" id="viewRawButton" onclick="viewRaw(patchviewerinstalled);" style="display: none;">[% L('View Attachment As Raw') %]<\/button>');
|
||||
}
|
||||
//-->
|
||||
</script>
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
</span>
|
||||
[% END %]
|
||||
<a href="attachment.cgi?bugid=[% bugid %]&action=enter" onclick="return to_attachment_page(this);">Add an attachment</a>
|
||||
<span id="att_multi_link">| <a href="javascript:void(0)" onclick="document.getElementById('att_multi_link').style.display='none';iframeajax('page.cgi?id=attach-multiple.html', {'bug_id' : [% bugid %]})">Add multiple</a></span>
|
||||
<span id="att_multi_link">| <a href="javascript:void(0)" onclick="document.getElementById('att_multi_link').style.display='none';iframeajax('page.cgi?id=attach-multiple.html', {'bug_id' : [% bugid %]})">Add multiple</a> </span>
|
||||
(proposed patch, testcase, etc.)
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -28,19 +28,19 @@
|
|||
|
||||
[% filtered_desc = bug.short_desc FILTER html %]
|
||||
[% PROCESS global/header.html.tmpl
|
||||
title = "Changes made to $terms.bug $bug.bug_id"
|
||||
header = "Activity log for $terms.bug $bug.bug_id: $filtered_desc"
|
||||
title = "Changes made to $terms.bug " _ bug.bug_id
|
||||
header = "Activity log for $terms.bug " _ bug.bug_id _ ": " _ filtered_desc
|
||||
%]
|
||||
|
||||
<p>
|
||||
[% "Back to $terms.bug $bug.bug_id" FILTER bug_link(bug) FILTER none %]
|
||||
[% "Back to $terms.bug " _ bug.bug_id FILTER bug_link(bug) FILTER none %]
|
||||
</p>
|
||||
|
||||
[% PROCESS bug/activity/table.html.tmpl %]
|
||||
|
||||
[% IF operations.size > 0 %]
|
||||
<p>
|
||||
[% "Back to $terms.bug $bug.bug_id" FILTER bug_link(bug) FILTER none %]
|
||||
[% "Back to $terms.bug " _ bug.bug_id FILTER bug_link(bug) FILTER none %]
|
||||
</p>
|
||||
[% END %]
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# License: Dual-license MPL 1.1+ or GPL 3.0+
|
||||
# Author(s): Vitaliy Filippov %]
|
||||
|
||||
[% INCLUDE global/header.html.tmpl title = "$terms.Bug $bug.id - Check access" %]
|
||||
[% INCLUDE global/header.html.tmpl title = terms.Bug _ " " _ bug.id _ " - Check access" %]
|
||||
|
||||
<h2>[% IF user_list.size > 0 %][% user_list.size %] [%+ user_list.size > 1 ? 'users' : 'user' %][% ELSE %]Everyone[% END %] can see <a href="show_bug.cgi?id=[% bug.id %]">[% terms.Bug _ " " _ bug.id %]</a></h2>
|
||||
|
||||
|
|
|
@ -192,8 +192,7 @@ function PutDescription() {
|
|||
height: 5em; overflow: auto;">
|
||||
<script type="text/javascript">
|
||||
if ((document.getElementById) && (document.body.innerHTML)) {
|
||||
document.write("\
|
||||
Select a component to see its description here.");
|
||||
document.write("[% L('Select a component to see its description here.') %]");
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
|
|
@ -36,8 +36,8 @@
|
|||
|
||||
[% IF NOT multiple_bugs AND NOT doall %]
|
||||
[% filtered_desc = short_desc FILTER html %]
|
||||
[% title = "$title for $terms.bug $bug_id"
|
||||
header = "$header for $terms.bug <a href=\"show_bug.cgi?id=$bug_id\">$bug_id</a>"
|
||||
[% title = title _ " for $terms.bug " _ bug_id
|
||||
header = header _ " for $terms.bug " _ "<a href=\"show_bug.cgi?id=$bug_id\">$bug_id</a>"
|
||||
subheader = filtered_desc
|
||||
%]
|
||||
[% END %]
|
||||
|
|
|
@ -23,9 +23,8 @@
|
|||
|
||||
[% filtered_desc = blocked_tree.$bugid.short_desc FILTER html %]
|
||||
[% PROCESS global/header.html.tmpl
|
||||
title = "Dependency tree for $terms.Bug $bugid"
|
||||
header = "Dependency tree for
|
||||
<a href=\"show_bug.cgi?id=$bugid\">$terms.Bug $bugid</a>"
|
||||
title = "Dependency tree for $terms.Bug " _ bugid
|
||||
header = "Dependency tree for $terms.Bug " _ "<a href=\"show_bug.cgi?id=$bugid\">$bugid</a>"
|
||||
javascript_urls = ["js/expanding-tree.js"]
|
||||
style_urls = ["skins/standard/dependency-tree.css"]
|
||||
subheader = filtered_desc
|
||||
|
|
|
@ -27,7 +27,7 @@ function addfield()
|
|||
opt = sel.options[sel.selectedIndex];
|
||||
tr = document.createElement('TR');
|
||||
td = document.createElement('TD');
|
||||
td.innerHTML = opt.text + ' for all bugs: ';
|
||||
td.innerHTML = opt.text + [% L(' for all bugs:') %] + ' ';
|
||||
tr.appendChild(td);
|
||||
td = document.createElement('TD');
|
||||
td.innerHTML = '<input type="text" name="f_' + opt.value + '" value="" />';
|
||||
|
@ -106,7 +106,7 @@ function checkColumns()
|
|||
break;
|
||||
}
|
||||
if (!chk)
|
||||
alert('Для импорта не выбрано ни одного бага из списка!');
|
||||
alert('[% L('No bugs selected for importing!') %]');
|
||||
return chk;
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
No, do not add the reporter to CC list on [% orig_bug FILTER none %]
|
||||
</p>
|
||||
<p>
|
||||
[% "Throw away my changes, and revisit $terms.bug $duplicate_bug_id"
|
||||
[% "Throw away my changes, and revisit $terms.bug " _ duplicate_bug_id
|
||||
FILTER bug_link(duplicate_bug_id) FILTER none %]
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
@ -94,7 +94,7 @@ You have the following choices:
|
|||
[% END %]
|
||||
<li>
|
||||
Throw away my changes, and
|
||||
[%+ "revisit $terms.bug $bug.id" FILTER bug_link(bug) FILTER none %]
|
||||
[%+ "revisit $terms.bug " _ bug.id FILTER bug_link(bug) FILTER none %]
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -43,14 +43,14 @@
|
|||
'?' => "requested"
|
||||
};
|
||||
titles = {
|
||||
'bug' => "Changes submitted for $link",
|
||||
'dupe' => "Duplicate notation added to $link",
|
||||
'dep' => "Checking for dependency changes on $link",
|
||||
'votes' => "$Link confirmed by number of votes",
|
||||
'created' => "$Link has been added to the database",
|
||||
'move' => "$Link has been moved to another database",
|
||||
'flag' => "Flag $new_flag.name " _ statuses.${new_flag.status},
|
||||
'votes-removed' => "Votes removed from $Link in accordance with new product settings",
|
||||
'bug' => "Changes submitted for " _ link,
|
||||
'dupe' => "Duplicate notation added to " _ link,
|
||||
'dep' => "Checking for dependency changes on " _ link,
|
||||
'votes' => Link _ " confirmed by number of votes",
|
||||
'created' => Link _ " has been added to the database",
|
||||
'move' => Link _ " has been moved to another database",
|
||||
'flag' => "Flag " _ new_flag.name _ " " _ statuses.${new_flag.status},
|
||||
'votes-removed' => "Votes removed from " _ Link _ " in accordance with new product settings",
|
||||
}
|
||||
%]
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
[% header = title _ header _ " (and $terms.bugs blocking it)" %]
|
||||
[% title = title _ "$terms.Bug $ids.0" %]
|
||||
[% ELSE %]
|
||||
[% title = title _ "($ids.size $terms.bugs selected)" %]
|
||||
[% title = title _ "(" _ ids.size _ " $terms.bugs selected)" %]
|
||||
[% header = title %]
|
||||
[% END %]
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ var field = [
|
|||
[%-# These values are meaningful for custom fields only. %]
|
||||
[% IF x.custom %]
|
||||
type: '[% x.type FILTER js %]',
|
||||
type_desc: '[% field_types.${x.type} FILTER js %]',
|
||||
type_desc: '[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.${x.type}} FILTER js %]',
|
||||
enter_bug: '[% x.enter_bug FILTER js %]',
|
||||
[% END %]
|
||||
},
|
||||
|
|
|
@ -257,7 +257,7 @@
|
|||
[%-# These values are meaningful for custom fields only. %]
|
||||
[% IF item.custom %]
|
||||
<bz:type>[% item.type FILTER html %]</bz:type>
|
||||
<bz:type_desc>[% field_types.${item.type} FILTER html %]</bz:type_desc>
|
||||
<bz:type_desc>[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.${item.type}} FILTER html %]</bz:type_desc>
|
||||
<bz:enter_bug>[% item.enter_bug FILTER html %]</bz:enter_bug>
|
||||
[% END %]
|
||||
</bz:field>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% Param('maintainer') %]
|
||||
Subject: [[% terms.Bugzilla %]] Account Lock-Out: [% locked_user.login %] ([% attempts.0.ip_addr %])
|
||||
Subject: [% "[$terms.Bugzilla] Account Lock-Out:" %] [% locked_user.login %] ([% attempts.0.ip_addr %])
|
||||
X-Bugzilla-Type: admin
|
||||
|
||||
The IP address [% attempts.0.ip_addr %] failed too many login attempts (
|
||||
|
|
|
@ -112,8 +112,8 @@ Bug [% d.dep %] summary: [% d.short_desc %]
|
|||
[% END %]
|
||||
|
||||
-- [%# Protect the trailing space of the signature marker %]
|
||||
Configure [% terms.bug %]mail: [% urlbase %]userprefs.cgi?tab=email
|
||||
------- You are receiving this mail because: -------
|
||||
Configure [% terms.bugmail %]: [% urlbase %]userprefs.cgi?tab=email
|
||||
------- [%+ "You are receiving this mail because:" %] -------
|
||||
[% FOREACH relationship = reasons %]
|
||||
[% SWITCH relationship %]
|
||||
[% CASE constants.REL_ASSIGNEE %]
|
||||
|
@ -266,7 +266,7 @@ body { font-family: Segoe UI, sans-serif; }
|
|||
[% END %]
|
||||
|
||||
<p style="font-size: 12px; font-style: italic; margin-bottom: 0">
|
||||
<a href="[% urlbase %]userprefs.cgi?tab=email">Configure [% terms.bug %]mail</a><br />
|
||||
<a href="[% urlbase %]userprefs.cgi?tab=email">Configure [% terms.bugmail %]</a><br />
|
||||
You are receiving this mail because:
|
||||
</p>
|
||||
<ul style="font-size: 12px; font-style: italic; margin: 0; padding-left: 2em"><li>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% addressee %]
|
||||
Subject: [[% terms.Bugzilla %]] Sanity Check Results
|
||||
Subject: [% "[$terms.Bugzilla] Sanity Check Results" %]
|
||||
X-Bugzilla-Type: sanitycheck
|
||||
|
||||
[%+ urlbase %]sanitycheck.cgi
|
||||
|
|
|
@ -21,8 +21,7 @@
|
|||
Content-Type: text/plain
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% user.email %]
|
||||
Subject: [[% terms.Bugzilla %]] Your account [% user.login -%]
|
||||
is being impersonated
|
||||
Subject: [% "[$terms.Bugzilla] Your account " _ user.login _ " is being impersonated" %]
|
||||
X-Bugzilla-Type: admin
|
||||
|
||||
[%+ sudoer.identity %] has used the 'sudo' feature to access
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param('mailfrom') %]
|
||||
To: [% to %]
|
||||
Subject: [% terms.Bug %] [%+ bugid %] Some or all of your votes have been removed.
|
||||
Subject: [% terms.Bug _ ' ' _ bugid _ " - Some or all of your votes have been removed." %]
|
||||
X-Bugzilla-Type: voteremoved
|
||||
|
||||
Some or all of your votes have been removed from [% terms.bug %] [%+ bugid %].
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
From: [% Param("mailfrom") %]
|
||||
To: [% email %][% Param("emailsuffix") %]
|
||||
Subject: Your [% terms.Bugzilla %] [%+ terms.bug %] list needs attention.
|
||||
Subject: [% "Your $terms.Bugzilla $terms.bug list needs attention." %]
|
||||
X-Bugzilla-Type: whine
|
||||
|
||||
[This e-mail has been automatically generated.]
|
||||
|
|
|
@ -217,8 +217,6 @@
|
|||
[% END %]
|
||||
|
||||
<script src="[% 'js/util.js' | ts_url %]" type="text/javascript"></script>
|
||||
<script src="[% 'js/global.js' | ts_url %]" type="text/javascript"></script>
|
||||
<script src="[% 'js/hinter.js' | ts_url %]" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
[%# The language selector needs javascript to set its cookie,
|
||||
|
@ -226,7 +224,8 @@
|
|||
# If the browser can run javascript, it will then "unhide"
|
||||
# the language selector using the following code.
|
||||
#%]
|
||||
function unhide_language_selector() {
|
||||
function unhide_language_selector()
|
||||
{
|
||||
removeClass('lang_links_container', 'bz_default_hidden');
|
||||
}
|
||||
addListener(window, 'load', unhide_language_selector);
|
||||
|
@ -238,17 +237,16 @@
|
|||
var BUGZILLA = {
|
||||
param: {
|
||||
cookiepath: '[% Param('cookiepath') FILTER js %]'
|
||||
},
|
||||
string: {
|
||||
attach_desc_required: 'You must enter a Description for this attachment.'
|
||||
}
|
||||
};
|
||||
[% IF javascript %]
|
||||
[% javascript %]
|
||||
[% javascript %]
|
||||
[% END %]
|
||||
// -->
|
||||
</script>
|
||||
|
||||
<script src="[% ('i18n/' _ LANGUAGE _ '/messages.js') | ts_url %]" type="text/javascript"></script>
|
||||
<script src="[% 'js/global.js' | ts_url %]" type="text/javascript"></script>
|
||||
<script src="[% 'js/hinter.js' | ts_url %]" type="text/javascript"></script>
|
||||
[% IF javascript_urls %]
|
||||
[% FOREACH javascript_url = javascript_urls %]
|
||||
<script src="[% javascript_url | ts_url | html %]" type="text/javascript"></script>
|
||||
|
@ -304,17 +302,17 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
[% IF Bugzilla.languages.size > 1 %]
|
||||
[% IF Bugzilla.i18n.supported_languages.size > 1 %]
|
||||
<table id="lang_links_container" cellpadding="0" cellspacing="0"
|
||||
class="bz_default_hidden"><tr><td>
|
||||
<ul class="links">
|
||||
[% FOREACH lang = Bugzilla.languages.sort %]
|
||||
[% FOREACH lang = Bugzilla.i18n.supported_languages.sort %]
|
||||
<li>[% IF NOT loop.first %]<span class="separator"> | </span>[% END %]
|
||||
[% IF lang == current_language %]
|
||||
[% IF lang == LANGUAGE %]
|
||||
<span class="lang_current">[% lang FILTER html FILTER upper %]</span>
|
||||
[% ELSE %]
|
||||
<a href="#" onclick="set_language('[% lang FILTER none %]');">
|
||||
[%- lang FILTER html FILTER upper %]</a>
|
||||
[%- lang FILTER html FILTER upper %]</a>
|
||||
[% END %]
|
||||
</li>
|
||||
[% END %]
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
[% END %]
|
||||
|
||||
[% BLOCK msg_changed_attachment %]
|
||||
[% SET title = "Changes Submitted to Attachment $id of $terms.Bug $bug_id" %]
|
||||
[% SET title = "Changes Submitted to Attachment " _ id _ " of $terms.Bug " _ bug_id %]
|
||||
<dl>
|
||||
<dt>Changes to <a href="attachment.cgi?id=[% id %]&action=edit">attachment [% id %]</a>
|
||||
of [% "$terms.bug $bug_id" FILTER bug_link(bug_id) FILTER none %] submitted
|
||||
|
@ -177,7 +177,7 @@
|
|||
[% IF changed_fields.size
|
||||
+ groups_added_to.size + groups_removed_from.size
|
||||
+ groups_granted_rights_to_bless.size + groups_denied_rights_to_bless.size %]
|
||||
[% title = "User $loginold updated" %]
|
||||
[% title = "User " _ loginold _ " updated" %]
|
||||
The following changes have been made to the user account
|
||||
[%+ loginold | html %]:
|
||||
<ul>
|
||||
|
@ -230,14 +230,14 @@
|
|||
[% END %]
|
||||
</ul>
|
||||
[% ELSE %]
|
||||
[% title = "User $otheruser.login not changed" %]
|
||||
[% title = "User " _ otheruser.login _ " not changed" %]
|
||||
You didn't request any changes to the user's account
|
||||
[%+ otheruser.login | html %].
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK msg_account_deleted %]
|
||||
[% title = "User $otheruser.login deleted" %]
|
||||
[% title = "User " _ otheruser.login _ " deleted" %]
|
||||
The user account [% otheruser.login | html %] has been deleted
|
||||
successfully.
|
||||
[% END %]
|
||||
|
@ -498,18 +498,6 @@
|
|||
[%+ flag_creation_error FILTER none %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK msg_get_field_desc %]
|
||||
[% field_descs.$field_name | html %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK msg_get_resolution %]
|
||||
[% resolution | html %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK msg_get_status %]
|
||||
[% status | html %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK msg_group_created %]
|
||||
[% title = "New Group Created" %]
|
||||
The group <em>[% group.name | html %]</em> has been created.
|
||||
|
|
|
@ -543,7 +543,7 @@
|
|||
|
||||
[% BLOCK error_useless_customfield_type %]
|
||||
[% title = "Can't create field of a useless type" %]
|
||||
It is impossible to create "[% lc_messages.field_types.$type %]" fields,
|
||||
It is impossible to create "[% lc_messages.field_types.${constants.FIELD_TYPE_NAMES.$type} %]" fields,
|
||||
because functionality of this type is hard-coded to a single builtin field.
|
||||
[% END %]
|
||||
|
||||
|
@ -574,16 +574,16 @@
|
|||
|
||||
[% BLOCK error_direct_field_needed_for_reverse %]
|
||||
[% title = "Invalid Direct Field selected" %]
|
||||
Each field of type "[% lc_messages.field_types.${constants.FIELD_TYPE_BUG_ID_REV} %]" must correspond
|
||||
to one, and only one field of type "[% lc_messages.field_types.${constants.FIELD_TYPE_BUG_ID} %]" and
|
||||
Each field of type "[% lc_messages.field_types.BUG_ID_REV %]" must correspond
|
||||
to one, and only one field of type "[% lc_messages.field_types.BUG_ID %]" and
|
||||
represent its "reverse relation". For example, it may be "Internal Bugs" for the corresponding
|
||||
"External Bug" field.
|
||||
[% END %]
|
||||
|
||||
[% BLOCK error_duplicate_reverse_field %]
|
||||
[% title = "Duplicate Reverse Field" %]
|
||||
It is prohibited to create more than one field of type "[% lc_messages.field_types.${constants.FIELD_TYPE_BUG_ID_REV} %]"
|
||||
corresponding to a single "[% lc_messages.field_types.${constants.FIELD_TYPE_BUG_ID} %]" type field.
|
||||
It is prohibited to create more than one field of type "[% lc_messages.field_types.BUG_ID_REV %]"
|
||||
corresponding to a single "[% lc_messages.field_types.BUG_ID %]" type field.
|
||||
[% END %]
|
||||
|
||||
[% BLOCK error_field_type_mismatch %]
|
||||
|
|
|
@ -34,6 +34,6 @@
|
|||
[%-# FIXME: remove hardcoded i18n message, also from js/field.js::userAutocomplete() %]
|
||||
new SimpleAutocomplete("[% id | js %]",
|
||||
function(h) { userAutocomplete(h, [% custom_userlist ? json(custom_userlist) : "null" %], [% Param('usemenuforusers') ? 1 : 0 %]); },
|
||||
{ emptyText: 'No users found'[% ', multipleDelimiter: ","' IF multiple %] });
|
||||
{ emptyText: L('No users found')[% ', multipleDelimiter: ","' IF multiple %] });
|
||||
//-->
|
||||
</script>
|
||||
|
|
|
@ -49,11 +49,11 @@ function addSidebar() {
|
|||
sidebarname="[% terms.Bugzilla %] "+sidebarname;
|
||||
window.sidebar.addPanel (sidebarname, "[% urlbase FILTER html %]sidebar.cgi", "");
|
||||
}
|
||||
var quicksearch_message = "Enter [% terms.abug %] # or some search terms";
|
||||
var quicksearch_message = '[% L('Enter $terms.abug # or some search terms') %]';
|
||||
|
||||
function checkQuicksearch( form ) {
|
||||
if (form.quicksearch.value == '' || form.quicksearch.value == quicksearch_message ) {
|
||||
alert('Please enter one or more search terms first.');
|
||||
alert('[% L('Please enter one or more search terms first.') %]');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -38,8 +38,8 @@ function SetCheckboxes(value)
|
|||
item.checked = value;
|
||||
}
|
||||
}
|
||||
document.write(' <input type="button" name="uncheck_all" value="Uncheck All" onclick="SetCheckboxes(false);" />');
|
||||
document.write(' <input type="button" name="check_all" value="Check All" onclick="SetCheckboxes(true);" />');
|
||||
document.write(' <input type="button" name="uncheck_all" value="[% L('Uncheck All') %]" onclick="SetCheckboxes(false);" />');
|
||||
document.write(' <input type="button" name="check_all" value="[% L('Check All') %]" onclick="SetCheckboxes(true);" />');
|
||||
//-->
|
||||
</script>
|
||||
|
||||
|
|
|
@ -144,9 +144,9 @@
|
|||
}
|
||||
}
|
||||
document.write(' <input type="button" name="uncheck_all" '
|
||||
+'value="Uncheck All" onclick="SetCheckboxes(false);" />');
|
||||
+'value="[% L('Uncheck All') %]" onclick="SetCheckboxes(false);" />');
|
||||
document.write(' <input type="button" name="check_all" '
|
||||
+'value="Check All" onclick="SetCheckboxes(true);" />');
|
||||
+'value="[% L('Check All') %]" onclick="SetCheckboxes(true);" />');
|
||||
//--></script>
|
||||
|
||||
<input type="submit" id="update" value="Save Changes" />
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
addListener(window, 'load', function() {
|
||||
filter_[% id %] = new SimpleAutocomplete("usr_filter_[% id %]",
|
||||
function(h) { fieldBuglistAutocomplete(h, "[% id %]", emptyOptions[% id %]); },
|
||||
{ emptyText: 'No keywords found', multipleDelimiter: "," }
|
||||
{ emptyText: '[% L('No values found') %]', multipleDelimiter: "," }
|
||||
);
|
||||
});
|
||||
//-->
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
{ name => "id", description => "$terms.Bug #" },
|
||||
{ name => "count", description => "Dupe<br />Count" },
|
||||
{ name => "delta",
|
||||
description => "Change in last<br />$changedsince day(s)" },
|
||||
description => "Change in last<br />" _ changedsince _ " day(s)" },
|
||||
{ name => "component", description => field_descs.component },
|
||||
{ name => "bug_severity", description => field_descs.bug_severity },
|
||||
] %]
|
||||
|
|
|
@ -47,13 +47,13 @@ var queryform = "queryform"
|
|||
[%# The decent help requires Javascript %]
|
||||
<script type="text/javascript"> <!--
|
||||
[% IF NOT Bugzilla.cgi.param("help") %]
|
||||
document.write("<p><a href='query.cgi?help=1&format=advanced'>Give me some help<\/a> (reloads page).<\/p>");
|
||||
document.write("<p><a href='query.cgi?help=1&format=advanced'>[% L('Give me some help') %]<\/a> [% L('(reloads page).') %]<\/p>");
|
||||
[% ELSE %]
|
||||
[% PROCESS "search/search-help.html.tmpl" %]
|
||||
if (generateHelp())
|
||||
document.write("<p>For help, mouse over the page elements.<\/p>");
|
||||
document.write("<p>[% L('For help, mouse over the page elements.') %]<\/p>");
|
||||
else
|
||||
document.write("<p>Help initialization failed, no help available.<\/p>");
|
||||
document.write("<p>[% L('Help initialization failed, no help available.') %]<\/p>");
|
||||
[% END %]
|
||||
// -->
|
||||
</script>
|
||||
|
|
|
@ -167,7 +167,7 @@ function divide_hours_click()
|
|||
var sum = bzParseTime(document.getElementById('divide_hours').value);
|
||||
if (!sum || sum != sum)
|
||||
{
|
||||
alert('Нечего распределять! Введите число, время в формате HH:MM, или в днях 1d, 2d, 3d и т.п.');
|
||||
alert('[% L('Нечего распределять! Введите число, время в формате HH:MM, или в днях 1d, 2d, 3d и т.п.') %]');
|
||||
return;
|
||||
}
|
||||
if (!period_times_sorted)
|
||||
|
|
Loading…
Reference in New Issue