code:
#! /bin/false

# vim: set autoindent shiftwidth=4 tabstop=4:
# $Id: gettext_pp.pm,v 1.1 2009-11-01 19:00:00 pertusus Exp $

# Pure Perl implementation of Uniforum message translation.
# Copyright (C) 2002-2009 Guido Flohr <guido@imperia.net>,
# all rights reserved.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.

# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.

package Locale::gettext_pp;

use strict;

require 5.004;

use vars qw ($__gettext_pp_default_dir
			 $__gettext_pp_textdomain
			 $__gettext_pp_domain_bindings
			 $__gettext_pp_domain_codeset_bindings
			 $__gettext_pp_domains
			 $__gettext_pp_recoders
			 $__gettext_pp_unavailable_dirs
			 $__gettext_pp_domain_cache
			 $__gettext_pp_alias_cache
			 $__gettext_pp_context_glue);

use locale;

BEGIN {
	$__gettext_pp_textdomain = 'messages';
	$__gettext_pp_domain_bindings = {};
	$__gettext_pp_domain_codeset_bindings = {};
	$__gettext_pp_domains = {};
	$__gettext_pp_recoders = {};
	$__gettext_pp_unavailable_dirs = {};
	$__gettext_pp_domain_cache = {};
	$__gettext_pp_alias_cache = {};
	# The separator between msgctxt and msgid in a .mo file.  */
	$__gettext_pp_context_glue = "\004";

	$__gettext_pp_default_dir = '';

	for my $dir (qw (/usr/share/locale /usr/local/share/locale)) {
		if (-d $dir) {
			$__gettext_pp_default_dir = $dir;
			last;
		}
	}
}

BEGIN {
		require POSIX;
		require Exporter;
		use IO::Handle;
		require Locale::Recode;

		local $@;
		my ($has_messages, $five_ok);

		$has_messages = eval '&POSIX::LC_MESSAGES';

		unless (defined $has_messages && length $has_messages) {
				$five_ok = ! grep {my $x = eval "&POSIX::$_" || 0; $x eq '5';}
								qw (LC_CTYPE
								   LC_NUMERIC
								   LC_TIME
								   LC_COLLATE
								   LC_MONETARY
								   LC_ALL);
			if ($five_ok) {
				$five_ok = POSIX::setlocale (5, '');
			}
		}

		if (defined $has_messages && length $has_messages) {
eval <<'EOF';
sub LC_MESSAGES()
{
	local $!; # Do not clobber errno!

	return &POSIX::LC_MESSAGES;
}
EOF
		} elsif ($five_ok) {
eval <<'EOF';
sub LC_MESSAGES()
{
	local $!; # Do not clobber errno!

	# Hack: POSIX.pm deems LC_MESSAGES an invalid macro until
	# Perl 5.8.0.  However, on LC_MESSAGES should be 5 ...
	return 5;
}
EOF
		} else {
eval <<'EOF';
sub LC_MESSAGES()
{
	local $!; # Do not clobber errno!

	# This fallback value is widely used,
	# when LC_MESSAGES is not available.
	return 1729;
}
EOF
		}
}

use vars qw (%EXPORT_TAGS @EXPORT_OK @ISA $VERSION);

%EXPORT_TAGS = (locale_h => [ qw (
								  gettext
								  dgettext
								  dcgettext
								  ngettext
								  dngettext
								  dcngettext
								  pgettext
								  dpgettext
								  dcpgettext
								  npgettext
								  dnpgettext
								  dcnpgettext
								  textdomain
								  bindtextdomain
								  bind_textdomain_codeset
								  )
							  ],
				libintl_h => [ qw (LC_CTYPE
								   LC_NUMERIC
								   LC_TIME
								   LC_COLLATE
								   LC_MONETARY
								   LC_MESSAGES
								   LC_ALL)
							   ],
				);

@EXPORT_OK = qw (gettext
				 dgettext
				 dcgettext
				 ngettext
				 dngettext
				 dcngettext
				 pgettext
				 dpgettext
				 dcpgettext
				 npgettext
				 dnpgettext
				 dcnpgettext
				 textdomain
				 bindtextdomain
				 bind_textdomain_codeset
                 nl_putenv
				 LC_CTYPE
				 LC_NUMERIC
				 LC_TIME
				 LC_COLLATE
				 LC_MONETARY
				 LC_MESSAGES
				 LC_ALL);
@ISA = qw (Exporter);

my $has_nl_langinfo;

sub __load_catalog;
sub __load_domain;
sub __locale_category;

sub LC_NUMERIC()
{
	&POSIX::LC_NUMERIC;
}

sub LC_CTYPE()
{
	&POSIX::LC_CTYPE;
}

sub LC_TIME()
{
	&POSIX::LC_TIME;
}

sub LC_COLLATE()
{
	&POSIX::LC_COLLATE;
}

sub LC_MONETARY()
{
	&POSIX::LC_MONETARY;
}

sub LC_ALL()
{
	&POSIX::LC_ALL;
}

sub textdomain(;$)
{
	my $new_domain = shift;

	$__gettext_pp_textdomain = $new_domain if defined $new_domain &&
		length $new_domain;

	return $__gettext_pp_textdomain;
}

sub bindtextdomain($;$)
{
	my ($domain, $directory) = @_;

	my $retval;
	if (defined $domain && length $domain) {
		if (defined $directory && length $directory) {
			$retval = $__gettext_pp_domain_bindings->{$domain}
				= $directory;
		} elsif (exists $__gettext_pp_domain_bindings->{$domain}) {
			$retval = $__gettext_pp_domain_bindings->{$domain};
		} else {
			$retval = $__gettext_pp_default_dir;
		}
		$retval = '/usr/share/locale' unless defined $retval &&
			length $retval;
		return $retval;
	} else {
		return;
	}
}

sub bind_textdomain_codeset($;$)
{
	my ($domain, $codeset) = @_;

	if (defined $domain && length $domain) {
		if (defined $codeset && length $codeset) {
			return $__gettext_pp_domain_codeset_bindings->{$domain} = $codeset;
		} elsif (exists $__gettext_pp_domain_codeset_bindings->{$domain}) {
			return $__gettext_pp_domain_codeset_bindings->{$domain};
		}
	}

	return;
}

sub gettext($)
{
	my ($msgid) = @_;

	return dcnpgettext ('', undef, $msgid, undef, undef, undef);
}

sub dgettext($$)
{
	my ($domainname, $msgid) = @_;

	return dcnpgettext ($domainname, undef, $msgid, undef, undef, undef);
}

sub dcgettext($$$)
{
	my ($domainname, $msgid, $category) = @_;

	return dcnpgettext ($domainname, undef, $msgid, undef, undef, undef);
}

sub ngettext($$$)
{
	my ($msgid, $msgid_plural, $n) = @_;

	return dcnpgettext ('', undef, $msgid, $msgid_plural, $n, undef);
}

sub dngettext($$$$)
{
	my ($domainname, $msgid, $msgid_plural, $n) = @_;

	return dcnpgettext ($domainname, undef, $msgid, $msgid_plural, $n, undef);
}

sub dcngettext($$$$$)
{
	my ($domainname, $msgid, $msgid_plural, $n, $category) = @_;

	return dcnpgettext ($domainname, undef, $msgid, $msgid_plural, $n, , $category);
}

sub pgettext($$)
{
	my ($msgctxt, $msgid) = @_;

	return dcnpgettext ('', $msgctxt, $msgid, undef, undef, undef);
}

sub dpgettext($$$)
{
	my ($domainname, $msgctxt, $msgid) = @_;

	return dcnpgettext ($domainname, $msgctxt, $msgid, undef, undef, undef);
}

sub dcpgettext($$$$)
{
	my ($domainname, $msgctxt, $msgid, $category) = @_;

	return dcnpgettext ($domainname, $msgctxt, $msgid, undef, undef, undef);
}

sub npgettext($$$$)
{
	my ($msgctxt, $msgid, $msgid_plural, $n) = @_;

	return dcnpgettext ('', $msgctxt, $msgid, $msgid_plural, $n, undef);
}

sub dnpgettext($$$$$)
{
	my ($domainname, $msgctxt, $msgid, $msgid_plural, $n) = @_;

	return dcnpgettext ($domainname, $msgctxt, $msgid, $msgid_plural, $n, undef);
}

sub dcnpgettext($$$$$$)
{
	my ($domainname, $msgctxt, $msgid, $msgid_plural, $n, $category) = @_;

	return unless defined $msgid;

	my $plural = defined $msgid_plural;
	my $msg_ctxt_id = defined $msgctxt ? join($__gettext_pp_context_glue, ($msgctxt, $msgid)) : $msgid;

	local $!; # Do not clobber errno!

	# This is also done in __load_domain but we need a proper value.
	$domainname = 	$__gettext_pp_textdomain
		unless defined $domainname && length $domainname;

	# Category is always LC_MESSAGES (other categories are ignored).
	my $category_name = 'LC_MESSAGES';
	$category = LC_MESSAGES;

	my $domains = __load_domain ($domainname, $category, $category_name);

	my @trans = ();
	my $domain;
	my $found;
	foreach my $this_domain (@$domains) {
		if ($this_domain && defined $this_domain->{messages}->{$msg_ctxt_id}) {
			@trans = @{$this_domain->{messages}->{$msg_ctxt_id}};
			shift @trans;
			$domain = $this_domain;
			$found = 1;
			last;
		}
	}
	@trans = ($msgid, $msgid_plural) unless @trans;

	my $trans = $trans[0];
	if ($plural) {
		if ($domain) {
			my $nplurals = 0;
			($nplurals, $plural) = &{$domain->{plural_func}} ($n);
			$plural = 0 unless defined $plural;
			$nplurals = 0 unless defined $nplurals;
			$plural = 0 if $nplurals <= $plural;
		} else {
			$plural = $n != 1 || 0;
		}

		$trans = $trans[$plural] if defined $trans[$plural];
	}

	if ($found && defined $domain->{po_header}->{charset}) {
		my $input_codeset = $domain->{po_header}->{charset};
		# Convert into output charset.
		my $output_codeset = $__gettext_pp_domain_codeset_bindings->{$domainname};

		$output_codeset = $ENV{OUTPUT_CHARSET} unless defined $output_codeset;
		$output_codeset = __get_codeset ($category, $category_name,
										 $domain->{locale_id})
			unless defined $output_codeset;

		unless (defined $output_codeset) {
			# Still no point.
			my $lc_ctype = __locale_category (POSIX::LC_CTYPE(),
										   'LC_CTYPE');
			$output_codeset = $1
				if $lc_ctype =~ /^[a-z]{2}(?:_[A-Z]{2})?\.([^@]+)/;
		}

		# No point. :-(
		$output_codeset = $domain->{po_header}->{charset}
			unless defined $output_codeset;

		if (exists $__gettext_pp_domain_cache->{$output_codeset}) {
			$output_codeset = $__gettext_pp_domain_cache->{$output_codeset};
		} else {
			$output_codeset = 'utf-8' if lc $output_codeset eq 'utf8';
			$output_codeset =
				$__gettext_pp_domain_cache->{$output_codeset} =
				Locale::Recode->resolveAlias ($output_codeset);
		}

		if (defined $output_codeset &&
			$output_codeset ne $domain->{po_header}->{charset}) {
			# We have to convert.
			my $recoder;

			if (exists
				$__gettext_pp_recoders->{$input_codeset}->{$output_codeset}) {
				$recoder = $__gettext_pp_recoders->{$input_codeset}->{$output_codeset};
			} else {
				$recoder =
					$__gettext_pp_recoders->{$input_codeset}->{$output_codeset} =
					Locale::Recode->new (from => $input_codeset,
										 to => $output_codeset,
										 );
			}

			$recoder->recode ($trans);
		}
	}

	return $trans;
}

sub nl_putenv ($)
{
    my ($envspec) = @_;
    return unless defined $envspec;
    return unless length $envspec;
    return if substr ($envspec, 0, 1) eq '=';

    my ($var, $value) = split /=/, $envspec, 2;

    # In Perl we *could* set empty environment variables even under
    # MS-DOS, but for compatibility reasons, we implement the
    # brain-damaged behavior of the Microsoft putenv().
    if ($^O eq 'MSWin32') {
        $value = '' unless defined $value;
        if (length $value) {
            $ENV{$var} = $value;
        } else {
            delete $ENV{$var};
        }
    } else {
        if (defined $value) {
            $ENV{$var} = $value;
        } else {
            delete $ENV{$var};
        }
    }

    return 1;
}

sub __load_domain
{
	my ($domainname, $category, $category_name) = @_;

	$domainname = $__gettext_pp_textdomain
		unless defined $domainname && length $domainname;

	my $dir = bindtextdomain ($domainname, '');
	$dir = $__gettext_pp_default_dir unless defined $dir && length $dir;
	return [] unless defined $dir && length $dir;

	my @locales;
	my $cache_key;

	if (defined $ENV{LANGUAGE} && length $ENV{LANGUAGE}) {
		@locales = split /:/, $ENV{LANGUAGE};
		$cache_key = $ENV{LANGUAGE};
	} else {
		@locales = $cache_key = __locale_category ($category, $category_name);
	}

	# Have we looked that one up already?
	my $domains = $__gettext_pp_domain_cache->{$dir}->{$cache_key}->{$category_name}->{$domainname};

	if (@locales && !defined $domains) {
		my @dirs = ($dir);
		my @tries = (@locales);
		my %locale_lookup = map { $_ => $_ } @tries;

		foreach my $locale (@locales) {
			if ($locale =~ /^([a-z][a-z])
				(?:(_[A-Z][A-Z])?
				 (\.[-_A-Za-z0-9]+)?
				 )?
				(\@[-_A-Za-z0-9]+)?$/x) {

				if (defined $3) {
					defined $2 ?
						push @tries, $1 . $2 . $3 : push @tries, $1 . $3;
				}
				if (defined $2) {
					push @tries, $1 . $2;
					$locale_lookup{$1 . $2} = $locale;
				}
				if (defined $1) {
					push @tries, $1 if defined $1;
					$locale_lookup{$1} = $locale;
				}
			}
		}

		push @dirs, $__gettext_pp_default_dir
			if $__gettext_pp_default_dir && $dir ne $__gettext_pp_default_dir;

		my %seen = ();
		foreach my $basedir (@dirs) {
			foreach my $try (@tries) {
				my $fulldir = "$basedir/$try/$category_name";

				next if $seen{$fulldir}++;

				# If the cache for unavailable directories is removed,
				# the three lines below should be replaced by:
				# 'next unless -d $fulldir;'
				next if $__gettext_pp_unavailable_dirs->{$fulldir};
				++$__gettext_pp_unavailable_dirs->{$fulldir} and next
						unless -d $fulldir;

				my $domain = __load_catalog $fulldir, $domainname;
				next unless $domain;

				unless (defined $domain->{po_header}->{charset} &&
						length $domain->{po_header}->{charset} &&
						$try =~ /^(?:[a-z][a-z])
						(?:(?:_[A-Z][A-Z])?
						 (\.[-_A-Za-z0-9]+)?
						 )?
						(?:\@[-_A-Za-z0-9]+)?$/x) {
					$domain->{po_header}->{charset} = $1;
				}

				if (defined $domain->{po_header}->{charset}) {
					$domain->{po_header}->{charset} =
						Locale::Recode->resolveAlias ($domain->{po_header}->{charset});
				}
				$domain->{locale_id} = $locale_lookup{$try};
				push @$domains, $domain;
			}
		}
		$__gettext_pp_domain_cache->{$dir}->{$cache_key}->{$category_name}->{$domainname} = $domains;
	}

	$domains = [] unless defined $domains;
	return $domains;
}

sub __load_catalog
{
	my ($directory, $domainname) = @_;

	my $filename = "$directory/$domainname.mo";

	# Alternatively we could check the filename for evil characters ...
	# (Important for CGIs).
	return unless -f $filename && -r $filename;

	local $/;
	local *HANDLE;

	open HANDLE, "<$filename"
		or return;
	binmode HANDLE;
	my $raw = <HANDLE>;
	close HANDLE;

	# Corrupted?
	return if ! defined $raw || length $raw < 28;

	my $filesize = length $raw;

	# Read the magic number in order to determine the byte order.
	my $domain = {};
	my $unpack = 'N';
	$domain->{potter} = unpack $unpack, substr $raw, 0, 4;

	if ($domain->{potter} == 0xde120495) {
		$unpack = 'V';
	} elsif ($domain->{potter} != 0x950412de) {
		return;
	}
	my $domain_unpack = $unpack x 6;

	my ($revision, $num_strings, $msgids_off, $msgstrs_off,
		$hash_size, $hash_off) =
			unpack (($unpack x 6), substr $raw, 4, 24);

	return unless $revision == 0; # Invalid revision number.

	$domain->{revision} = $revision;
	$domain->{num_strings} = $num_strings;
	$domain->{msgids_off} = $msgids_off;
	$domain->{msgstrs_off} = $msgstrs_off;
	$domain->{hash_size} = $hash_size;
	$domain->{hash_off} = $hash_off;

	return if $msgids_off + 4 * $num_strings > $filesize;
	return if $msgstrs_off + 4 * $num_strings > $filesize;

	my @orig_tab = unpack (($unpack x (2 * $num_strings)),
						   substr $raw, $msgids_off, 8 * $num_strings);
	my @trans_tab = unpack (($unpack x (2 * $num_strings)),
							substr $raw, $msgstrs_off, 8 * $num_strings);

	my $messages = {};

	for (my $count = 0; $count < 2 * $num_strings; $count += 2) {
		my $orig_length = $orig_tab[$count];
		my $orig_offset = $orig_tab[$count + 1];
		my $trans_length = $trans_tab[$count];
		my $trans_offset = $trans_tab[$count + 1];

		return if $orig_offset + $orig_length > $filesize;
		return if $trans_offset + $trans_length > $filesize;

		my @origs = split /\000/, substr $raw, $orig_offset, $orig_length;
		my @trans = split /\000/, substr $raw, $trans_offset, $trans_length;

		# The singular is the key, the plural plus all translations is the
		# value.
		my $msgid = $origs[0];
		$msgid = '' unless defined $msgid && length $msgid;
		my $msgstr = [ $origs[1], @trans ];
		$messages->{$msgid} = $msgstr;
	}

	$domain->{messages} = $messages;

	# Try to find po header information.
	my $po_header = {};
	my $null_entry = $messages->{''}->[1];
	if ($null_entry) {
		my @lines = split /\n/, $null_entry;
		foreach my $line (@lines) {
			my ($key, $value) = split /:/, $line, 2;
			$key =~ s/-/_/g;
			$po_header->{lc $key} = $value;
		}
	}
	$domain->{po_header} = $po_header;

	if (exists $domain->{po_header}->{content_type}) {
		my $content_type = $domain->{po_header}->{content_type};
		if ($content_type =~ s/.*=//) {
			$domain->{po_header}->{charset} = $content_type;
		}
	}

	my $code = $domain->{po_header}->{plural_forms} || '';

	# Whitespace, locale-independent.
	my $s = '[ \t\r\n\013\014]';

	# Untaint the plural header.
	# Keep line breaks as is (Perl 5_005 compatibility).
	if ($code =~ m{^($s*
					 nplurals$s*=$s*[0-9]+
					 $s*;$s*
					 plural$s*=$s*(?:$s|[-\?\|\&=!<>+*/\%:;a-zA-Z0-9_\(\)])+
					 )}xms) {
		$domain->{po_header}->{plural_forms} = $1;
	} else {
		$domain->{po_header}->{plural_forms} = '';
	}

	# Determine plural rules.
	# The leading and trailing space is necessary to be able to match
	# against word boundaries.
	my $plural_func;

	if ($domain->{po_header}->{plural_forms}) {
		my $code = ' ' . $domain->{po_header}->{plural_forms} . ' ';
		$code =~
			s/([^_a-zA-Z0-9]|\A)([_a-z][_A-Za-z0-9]*)([^_a-zA-Z0-9])/$1\$$2$3/g;

		$code = "sub { my \$n = shift;
				   my (\$plural, \$nplurals);
				   $code;
				   return (\$nplurals, \$plural ? \$plural : 0); }";

		# Now try to evaluate the code.	 There is no need to run the code in
		# a Safe compartment.  The above substitutions should have destroyed
		# all evil code.  Corrections are welcome!
		$plural_func = eval $code;
		undef $plural_func if $@;
	}

	# Default is Germanic plural (which is incorrect for French).
	$plural_func = eval "sub { (2, 1 != shift || 0) }" unless $plural_func;

	$domain->{plural_func} = $plural_func;

	return $domain;
}

sub __locale_category
{
	my ($category, $category_name) = @_;

	local $@;
	my $value = eval {POSIX::setlocale ($category)};

	# We support only XPG syntax, i. e.
	# language[_territory[.codeset]][@modifier].
	undef $value unless (defined $value &&
						 length $value &&
						 $value =~ /^[a-z][a-z]
						 (?:_[A-Z][A-Z]
						  (?:\.[-_A-Za-z0-9]+)?
						  )?
						 (?:\@[-_A-Za-z0-9]+)?$/x);

	unless ($value) {
		$value = $ENV{LC_ALL};
		$value = $ENV{$category_name} unless defined $value && length $value;
		$value = $ENV{LANG} unless defined $value && length $value;
		return 'C' unless defined $value && length $value;
	}

	return $value if $value ne 'C' && $value ne 'POSIX';
}

sub __get_codeset
{
	my ($category, $category_name, $locale_id) = @_;

	local $@;
	unless (defined $has_nl_langinfo) {
		eval {
			require I18N::Langinfo;
		};
		$has_nl_langinfo = !$@;
	}

	if ($has_nl_langinfo) {
		# Try to set the locale via the specified id.
		my $saved_locale = eval { POSIX::setlocale (LC_ALL) };
		my $saved_lc_all = $ENV{LC_ALL};

		# Now try to set the locale via the environment.  There is no
		# point in calling the langinfo routines if this fails.
		$ENV{LC_ALL} = $locale_id;
		my $codeset;
		my $lc_all = eval { POSIX::setlocale (LC_ALL, $locale_id); };
		$codeset = I18N::Langinfo::langinfo (I18N::Langinfo::CODESET())
			if defined $lc_all;

		if ($saved_locale) {
			eval { POSIX::setlocale (LC_ALL, $saved_locale); }
		}
		return $codeset;
	}

	return;
}

1;

__END__

=head1 NAME

Locale::gettext_pp - Pure Perl Implementation of Uniforum Message Translation

=head1 SYNOPSIS

 use gettext_pp (:locale_h :libintl_h);

 gettext $msgid;
 dgettext $domainname, $msgid;
 dcgettext $domainname, $msgid, LC_MESSAGES;
 ngettext $msgid, $msgid_plural, $count;
 dngettext $domainname, $msgid, $msgid_plural, $count;
 dcngettext $domainname, $msgid, $msgid_plural, $count, LC_MESSAGES;
 pgettext $msgctxt, $msgid;
 dpgettext $domainname, $msgctxt, $msgid;
 dcpgettext $domainname, $msgctxt, $msgid, LC_MESSAGES;
 npgettext $msgctxt, $msgid, $msgid_plural, $count;
 dnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count;
 dcnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count, LC_MESSAGES;
 textdomain $domainname;
 bindtextdomain $domainname, $directory;
 bind_textdomain_codeset $domainname, $encoding;
 my $category = LC_CTYPE;
 my $category = LC_NUMERIC;
 my $category = LC_TIME;
 my $category = LC_COLLATE;
 my $category = LC_MONETARY;
 my $category = LC_MESSAGES;
 my $category = LC_ALL;

=head1 DESCRIPTION

The module B<Locale::gettext_pp> is the low-level interface to
message translation according to the Uniforum approach that is
for example used in GNU gettext and Sun's Solaris.

Normally you should not use this module directly, but the high
level interface Locale::TextDomain(3) that provides a much simpler
interface.	This description is therefore deliberately kept
brief.	Please refer to the GNU gettext documentation available at
L<http://www.gnu.org/manual/gettext/> for in-depth and background
information on the topic.

=head1 FUNCTIONS

The module exports by default nothing.	Every function has to be
imported explicitely or via an export tag (L<"EXPORT TAGS">).

=over 4

=item B<gettext MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dgettext TEXTDOMAIN, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcgettext TEXTDOMAIN, MSGID, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<ngettext MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dngettext TEXTDOMAIN, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcngettext TEXTDOMAIN, MSGID, MSGID_PLURAL, COUNT, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<pgettext MSGCTXT, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dpgettext TEXTDOMAIN, MSGCTXT, MSGID>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcpgettext TEXTDOMAIN, MSGCTXT, MSGID, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<npgettext MSGCTXT, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dnpgettext TEXTDOMAIN, MSGCTXT, MSGID, MSGID_PLURAL, COUNT>

See L<Locale::Messages/FUNCTIONS>.

=item B<dcnpgettext TEXTDOMAIN, MSGCTXT, MSGID, MSGID_PLURAL, COUNT, CATEGORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<textdomain TEXTDOMAIN>

See L<Locale::Messages/FUNCTIONS>.

=item B<bindtextdomain TEXTDOMAIN, DIRECTORY>

See L<Locale::Messages/FUNCTIONS>.

=item B<bind_textdomain_codeset TEXTDOMAIN, ENCODING>

=item B<nl_putenv ENVSPEC>

See L<Locale::Messages/FUNCTIONS>.

=back

=head1 CONSTANTS

You can (maybe) get the same constants from POSIX(3); see there for
a detailed description

=over 4

=item B<LC_CTYPE>

=item B<LC_NUMERIC>

=item B<LC_TIME>

=item B<LC_COLLATE>

=item B<LC_MONETARY>

=item B<LC_MESSAGES>

=item B<LC_ALL>

See L<Locale::Messages/CONSTANTS> for more information.

=back

=head1 EXPORT TAGS

This module does not export anything unless explicitely requested.
You can import groups of functions via two tags:

=over 4

=item B<use Locale::gettext_pp (':locale_h')>

Imports the functions that are normally defined in the C include
file F<locale.h>:

=over 8

=item B<gettext()>

=item B<dgettext()>

=item B<dcgettext()>

=item B<ngettext()>

=item B<dngettext()>

=item B<dcngettext()>

=item B<pgettext()>

Introduced with libintl-perl 1.17.

=item B<dpgettext()>

Introduced with libintl-perl 1.17.

=item B<dcpgettext()>

Introduced with libintl-perl 1.17.

=item B<npgettext()>

Introduced with libintl-perl 1.17.

=item B<dnpgettext()>

Introduced with libintl-perl 1.17.

=item B<dcnpgettext()>

Introduced with libintl-perl 1.17.

=item B<textdomain()>

=item B<bindtextdomain()>

=item B<bind_textdomain_codeset()>

=back

=item B<use Locale::gettext_pp (':libintl_h')>

Imports the locale category constants:

=over 8

=item B<LC_CTYPE>

=item B<LC_NUMERIC>

=item B<LC_TIME>

=item B<LC_COLLATE>

=item B<LC_MONETARY>

=item B<LC_MESSAGES>

=item B<LC_ALL>

=back

=back

=head1 AUTHOR

Copyright (C) 2002-2009, Guido Flohr E<lt>guido@imperia.netE<gt>, all
rights reserved.  See the source code for details.

This software is contributed to the Perl community by Imperia
(L<http://www.imperia.net/>).

=head1 SEE ALSO

Locale::TextDomain(3pm), Locale::Messages(3pm), Encode(3pm),
perllocale(3pm), POSIX(3pm), perl(1), gettext(1), gettext(3)

=cut

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
tab-width: 4
End:

=cut


code:
#! /bin/false

# vim: set autoindent shiftwidth=4 tabstop=4:
# $Id: gettext_xs.pod,v 1.1 2009-11-01 19:00:00 pertusus Exp $

# Pure Perl implementation of Uniforum message translation.
# Copyright (C) 2002-2009 Guido Flohr <guido@imperia.net>,
# all rights reserved.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.

# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.

=head1 NAME

Locale::gettext_xs - XS Implementation of Uniforum Message Translation

=head1 SYNOPSIS

 use gettext_xs (:locale_h :libintl_h);

 gettext $msgid;
 dgettext $domainname, $msgid;
 dcgettext $domainname, $msgid, LC_MESSAGES;
 ngettext $msgid, $msgid_plural, $count;
 dngettext $domainname, $msgid, $msgid_plural, $count;
 dcngettext $domainname, $msgid, $msgid_plural, $count, LC_MESSAGES;
 pgettext $msgctxt, $msgid;
 dpgettext $domainname, $msgctxt, $msgid;
 dcpgettext $domainname, $msgctxt, $msgid, LC_MESSAGES;
 npgettext $msgctxt, $msgid, $msgid_plural, $count;
 dnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count;
 dcnpgettext $domainname, $msgctxt, $msgid, $msgid_plural, $count, LC_MESSAGES;
 textdomain $domainname;
 bindtextdomain $domainname, $directory;
 bind_textdomain_codeset $domainname, $encoding;
 my $category = LC_CTYPE;
 my $category = LC_NUMERIC;
 my $category = LC_TIME;
 my $category = LC_COLLATE;
 my $category = LC_MONETARY;
 my $category = LC_MESSAGES;
 my $category = LC_ALL;

=head1 DESCRIPTION

The module B<Locale::gettext_xs> is the low-level interface to
message translation according to the Uniforum approach that is
for example used in GNU gettext and Sun's Solaris.

The module does not necessarily work on your system.  It depends
on the presence of shared libraries that are not always available.
The higher-level modules Locale::TextDomain::(3), resp.
Locale::Messages(3) will fall back to a pure Perl version if
boostrapping B<Locale::gettext_xs> fails.

The interface of B<Locale::gettext_xs> is mostly identical to the pure
Perl version as described in Locale::gettext_pp(3), see there for details.
Differences are outlined below.

B<Locale::gettext_xs> is downwards compatible to Locale::gettext(3)
by Phillip Vandry <vandry@Mlink.NET>.  You can use it as replacement
for Locale::gettext(3).

Please note that directory names passed to the function bindtextdomain()
are automatically converted from Perl semantics (slash as directory
separator) to local semantics (for example the backslash for MS-DOS).

=head1 AUTHOR

Copyright (C) 2002-2009, Guido Flohr E<lt>guido@imperia.netE<gt>, all
rights reserved.  See the source code for details.

The module is based on the work of Phillip Vandry <vandry@Mlink.NET>
in Locale::gettext(3).

This software is contributed to the Perl community by Imperia
(L<http://www.imperia.net/>).

=head1 SEE ALSO

Locale::TextDomain(3pm), Locale::gettext_pp(3pm), Locale::gettext(3pm),
Locale::Messages(3pm), File::Spec(3), perl(1)

=cut

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
tab-width: 4
End:

=cut

repo_name: mthl/texinfo

license: mit


code:
#! /bin/false

# vim: set autoindent shiftwidth=4 tabstop=4:
# $Id: libintlFAQ.pod,v 1.1 2009-11-01 19:00:00 pertusus Exp $

# High-level interface to Perl i18n.
# Copyright (C) 2002-2009 Guido Flohr <guido@imperia.net>,
# all rights reserved.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.

# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.

=head1 NAME

Locale::TextDomain::FAQ - Frequently asked questions for libintl-perl

=head1 DESCRIPTION

This FAQ

=head1 QUESTIONS AND ANSWERS

=head2 Why is libintl-perl so big?  Why don't you use Encode(3pm) for character
set conversion instead of rolling your own version?

Encode(3pm) requires at least Perl 5.7.x, whereas libintl-perl needs
to be operational on Perl 5.004.  Internally, libintl-perl uses Encode(3pm)
if it is available.

=head2 Why do the gettext functions always unset the utf-8 flag on the strings
it returns?

Because the gettext functions do not know whether the string is encoded
in utf-8 or not.  Instead of taking guesses, it rather unsets the flag.

=head2 Can I set the utf-8 flag on strings returned by the gettext family of
functions?

Yes, but it is not recommended.  If you absolutely want to do it,
use the function bind_textdomain_filter in Locale::Messages for it.

The strings returned by gettext and friends are by default encoded in
the preferred charset for the user's locale, but there is no portable
way to find out, whether this is utf-8 or not.  That means, you either
have to enforce utf-8 as the output character set (by means of
bind_textdomain_codeset() and/or the environment variable
OUTPUT_CHARSET) and override the user preference, or you run the risk
of marking strings as utf-8 which really aren't utf-8.

The whole concept behind that utf-8 flag introduced in Perl 5.6 is
seriously broken, and the above described dilemma is a proof for that.
The best thing you can do with that flag is get rid of it, and turn
it off.  Your code will benefit from it and become less error prone,
more portable and faster.

=head2 Why do non-ASCII characters in my Gtk2 application look messed up?

The Perl binding of Gtk2 has a design flaw.  It expects all UI messages
to be in UTF-8 and it also expects messages to be flagged as utf-8.  The
only solution for you is to enforce all your po files to be encoded
in utf-8 (convert them manually, if you need to), and also enforce that
charset in your application, regardless of the user's locale settings.
Assumed that your textdomain is "org.bar.foo", you have to code the
following into your main module or script:

  BEGIN {
      bind_textdomain_filter 'org.bar.foo', \&turn_utf_8_on;
      bind_textdomain_codeset 'org.bar.foo', 'utf-8';
  }

See the File GTestRunner.pm of Test::Unit::GTestRunner(3pm) for details.

=head2 How do I interface Glade2 UI definitions with libintl-perl?

Gtk2::GladeXML(3pm) seems to ignore calls to bind_textdomain().
See the File GTestRunner.pm of Test::Unit::GTestRunner(3pm) for a
possible solution.

=head2 Why does Locale::TextDomain use a double underscore?  I am used
to a single underscore from C or other languages.

Function names that consist of exactly one non-alphanumerical character
make the function automatically global in Perl.  Besides, in Perl
6 the concatenation operator will be the underscore instead of the
dot.

=head2 How do I switch languages or force a certain language independently
from user settings read from the environment?

The simple answer is:

    use POSIX qw (setlocale LC_ALL);

    my $language = 'fr';
    my $country = 'FR';
    my $charset = 'iso-8859-1';

    setlocale LC_ALL, "${language}_$country.$charset";

Sadly enough, this will fail in many cases.  The problem is that locale
identifiers are not standardized and are completely system-dependent.  Not
only their overall format, but also other details like case-sensitivity.
Some systems are very forgiving about the system - for example normalizing
charset descriptions - others very strict.  In order to be reasonably
platform independent, you should try a list of possible locale identifiers
for your desired settings.  This is about what I would try for achieving the
above:

   my @tries = qw (
   	fr_FR.iso-8859-1 fr_FR.iso8859-1 fr_FR.iso88591
	fr_FR.ISO-8859-1 fr_FR.ISO8859-1 fr_FR.ISO88591
   	fr.iso-8859-1 fr.iso8859-1 fr.iso88591
	fr.ISO-8859-1 fr.ISO8859-1 fr.ISO88591
	fr_FR
   	French_France.iso-8859-1 French_France.iso8859-1 French_France.iso88591
	French_France.ISO-8859-1 French_France.ISO8859-1 French_France.ISO88591
   	French.iso-8859-1 French.iso8859-1 French.iso88591
	French.ISO-8859-1 French.ISO8859-1 French.ISO88591
   );
   foreach my $try (@tries) {
   	last if setlocale LC_ALL, $try;
   }

Set Locale::Util(3pm) for functions that help you with this.

Alternatively, you can force a certain language by setting the environment
variables LANGUAGE, LANG and OUTPUT_CHARSET, but this is only guaranteed
to work, if you use the pure Perl implementation of gettext (see the
documentation for select_package() in Locale::Messages(3pm)). You would
do the above like this:

    use Locale::Messages qw (nl_putenv);

    # LANGUAGE is a colon separated list of languages.
    nl_putenv("LANGUAGE=fr_FR");

    # If LANGUAGE is set, LANG should be set to the primary language.
    # This is not needed for gettext, but for other parts of the system
    # it is.
    nl_putenv("LANG=fr_FR");

    # Force an output charset like this:
    nl_putenv("OUTPUT_CHARSET=iso-8859-1");

    setlocale (LC_MESSAGES, 'C');

These environment variables are GNU extensions, and they are also
honored by libintl-perl.  Still, you should always try to set the
locale with setlocale for the catch-all category LC_ALL.  If you miss
to do so, your program's output maybe cluttered, mixing languages
and charsets, if the system runs in a locale that is not compatible
with your own language settings.

Remember that these environment variables are not guaranteed to
work, if you use an XS version of gettext.  In order to force usage
of the pure Perl implementation, do the following:

    Locale::Messages->select_package ('gettext_pp');

If you think, this is brain-damaged, you are right, but I cannot help
you.  Actually there should be a more flexible API than setlocale,
but at the time of this writing there isn't.  Until then, the recommentation
goes like this:

	1) Try setting LC_ALL with Locale::Util.
	2) If that does not succeed, either give up or ...
	3) Reset LC_MESSAGES to C/POSIX.
	4) Switch to pure Perl for gettext.
        5) Set the environment variables LANGUAGE, LANG,
	   and OUTPUT_CHARSET to your desired values.

=head2 What is the advantage of libintl-perl over Locale::Maketext?

Of course, I can only give my personal opinion as an answer.

Locale::Maketext claims to fix design flaws in gettext.  These alleged
design flaws, however, boil down to one pathological case which always
has a workaround.  But both programmers and translators pay this
fix with an unnecessarily complicated interface.

The paramount advantage of libintl-perl is that it uses an approved
technology and concept.  Except for Java(tm) programs, this is the
state-of-the-art concept for localizing Un*x software.  Programmers
that have already localized software in C, C++, C#, Python, PHP,
or a number of other languages will feel instantly at home, when
localizing software written in Perl with libintl-perl.  The same
holds true for the translators, because the files they deal with
have exactly the same format as those for other programming languages.
They can use the same set of tools, and even the commands they have
to execute are the same.

With libintl-perl refactoring of the software is painless, even if
you modify, add or delete translatable strings.  The gettext tools
are powerful enough to reduce the effort of the translators to the
bare minimum.  Maintaining the message catalogs of Locale::Maketext
in larger scale projects, is IMHO unfeasible.

Editing the message catalogs of Locale::Maketext - they are really
Perl modules - asks too much from most translators, unless
they are programmers.  The portable object (po) files used by
libintl-perl have a simple syntax, and there are a bunch of specialized
GUI editors for these files, that facilitate the translation process
and hide most complexity from the user.

Furthermore, libintl-perl makes it possible to mix programming
languages without a paradigm shift in localization.  Without any special
efforts, you can write a localized software that has modules written
in C, modules in Perl, and builds a Gtk user interface with Glade.
All translatable strings end up in one single message catalog.

Last but not least, the interface used by libintl-perl is plain
simple:  Prepend translatable strings with a double underscore,
and you are done in most cases.

=head2 Why do single-quoted strings not work?

You probably write something like this:

    print __'Hello';

And you get an error message like "Can't find string terminator "'" anywhere
before EOF at ...", or even "Bareword found where operator expected at
... Might be a runaway multi-line '' string starting on".  The above line
is (really!) essentially the same as writing:

    print __::Hello';

A lesser know feature of Perl is that you can use a single quote ("'") as
the separator in packages instead of the double colon (":").  What the
Perl parser sees in the first example is a valid package name ("__")
followed by the separator ("'"), then another valid package name ("Hello")
followed by a lone single quote.  It is therefore not a problem in
libintl-perl but simple wrong Perl syntax.  You have to correct alternatives:

    print __ 'Hello';   # Insert a space to disambiguate.

Or use double-quotes:

    print __"Hello";

Thanks to Slavi Agafonkin for pointing me to the solution of this mystery.


code:
This is a simple, respectively stupid Perl package that shows how the
complete internationalization process for a Perl package *could* be
done.  It does not claim to be the smartest or the only possible
solution, but it provides at least a skeleton for real packages.  If
libintl-perl should someday become an "established" Perl package, it
would probably be a lot better to seamlessly integrate the process
into ExtUtils::MakeMaker, but for now it's all we have.

The example focuses on the packaging process, i. e. on the things you
have to do to maintain an internationalized Perl package, so that
users of your package will benefit from translations you provide.  It
therefore doesn't make use of any of the nitty-gritty details of
message translation like plural handling or the like.

Requirements
------------

The only requirement is a Perl aware version of GNU gettext.  Perl
support was introduced only recently in GNU gettext, and you will have
to check whether your copy of GNU gettext already supports Perl.
Support for Perl was introduced in version 0.12.2 of GNU gettext.  If
your version is older, you have to update GNU gettext.

First test
----------

The subdirectory "simplecal" contains a regular Perl package like the
ones you will find on the CPAN.  You should first try to build and
use the package:

     cd simplecal
     perl Makefile.PL
     make

If you see a warning that the prerequisite Locale::TextDomain is not
found, then you have to install libintl-perl first.

You should never "make install", the package is only a stupid example
and you will not really want to install it.  You can simply try it out
from the installation directory itself:

     perl -Ilib bin/simplecal.pl

It should print a crude calendar representation in English, or even in
your preferred language, depending on your system settings.

The Programming
---------------

Now we should dig into the sources.  All relevant files are commented
and should give you a pretty good idea of what's going on.  Change
your directory to the package directory "simplecal" and inspect the
source files.

The heart of the library is found in the file lib/SimpleCal.pm.  This
Perl module defines functions that map numeric values to month names
or abbreviated week day names.  You will find nothing unusual in this
module except for a line at the beginning of the file that reads:

     use Locale::TextDomain qw (org.imperia.simplecal);

In case you are not familiar with the operator "qw", this is an
equivalent writing of

     use Locale::TextDomain ('org.imperia.simplecal');

That line in the code does three things: It imports the module
Locale::TextDomain, *and* it states that the text domain (or
identifier) for this package is "org.imperia.simplecal", *and* it says
that the translations for this package can be found in the
subdirectory "LocaleDate" of any component of @INC (unless it can be
found in one of the system locations).  See the POD in
Locale::TextDomain for more information.

You may also find out that some strings have a "__" or a "N__" in
front of them.  The explanation to these funny things has two sides:
First, they mark the following strings as being translatable, so that the
parser "xgettext" included in GNU gettext can find them.  Yet, at runtime
both "__" and "N__" are really function names, and they will look up
their argument in the translation database.  There is more
documentation available on this.  Guess where! Yepp, in the POD of
Locale::TextDomain.

The library is used by a Perl script "bin/simplecal.pl".  Let's have a
look at that script now.  The first remarkable line is the one that
calls POSIX::setlocale():

     setlocale (LC_MESSAGES, '');

The POD of the POSIX module gives additional information on the
function setlocale().  In brief, that call initializes the locale
settings for the category "LC_MESSAGES" to the pre-selected user
settings (this is indicated by the empty second argument).  The
constant LC_MESSAGES is exported by Locale::Messages, which is always
a safe choice.  If your script is only intended to run with Perl 5.8
or better, you can also import LC_MESSAGES from the POSIX module.

The rest of the program only prints a calendar for the current month.
It retrieves the name of the month and the abbreviated weekday names
from our little SimpleCal.pm module which provides this information in
a localized form.

A Dutch Calendar
----------------

We want to see the calendar in Dutch now.  All you have to do is to
set the environment variable LANGUAGE to the value "nl".  If you don't
know how to do this, add the following line somewhere at the top of
"bin/simplecal.pl":

     $ENV{LANGUAGE} = "nl";

Now run the script again:

     perl -Ilib bin/simplecal.pl

It should print out the calendar in Dutch.  Look at the *.po files in
the subdirectory "po" for a list of other translations I have
prepared.  You can try them out in a similar manner.

Please see the file "README-NLS" in subdirectory "sample/simplecal"
for details on how to set the language via environment variables.

The Subdirectory "po"
---------------------

This directory contains the raw translations and a Makefile that will
compile and install them.  If you enter this directory and type "make"
you will see a list of the available Makefile targets.

The first one is the target "pot", a so-called phony target, i. e. it
is not related to a file with the name of "pot".  The command "make
pot" will remake the master catalog of the package and place the
result in the file "org.imperia.simplecal.pot"
("org.imperia.simplecal" is the text domain resp. identifier for our
package).  Type the command "make pot" now to see how the master
catalog is actually generated.  If the output says something like
"nothing to be done for `pot'", then delete the file
"org.imperia.simplecal.pot" and try again.

You should see now that the target file "org.imperia.simplecal.pot" is
generated by the program xgettext with a plethora of options:

     xgettext --output=./org.imperia.simplecal.pox --from-code=utf-8 \
              --add-comments=TRANSLATORS: --files-from=./POTFILES.in \
	      --copyright-holder="Imperia AG Huerth/Germany" \
	      --keyword --keyword='$__' --keyword=__ --keyword=__x \
	      --keyword=__n:1,2 --keyword=__nx:1,2 --keyword=__xn \
	      --keyword=N__ --language=perl && \
     rm -f org.imperia.simplecal.pot && \
     mv org.imperia.simplecal.pox org.imperia.simplecal.pot

Type "xgettext --help" for a detailled explanation of the command line
options.  In brief this invocation causes xgettext to read a list of
files from the file "POTFILES.in", extract all messages from these
source files and place the result in the output file
"org.imperia.simplecal.pox".  If the command succeeds, the old ".pot"
file is replaced by the new ".pox" file.

Yes, this is complicated, and that is why this skeleton Makefile is
provided here.  You can copy it without any modification into your
package to use it.

The file POTFILES.in contains a list of source files to be scanned for
translatable strings.  Have a look at it, and you will understand it.

The Makefile also includes a file called "PACKAGE".  This file contains
all package-dependent information in a couple of Makefile variables:

- TEXTDOMAIN
This Makefile variable should contain the text domain/identifier
for your package.  Please see the POD of Locale::TextDomain for advice
on a reasonable naming.

- LINGUAS
The language codes of all languages supported by your package.  Each
entry corresponds to a po file in the po subdirectory.

- COPYRIGHT_HOLDER
Usually your name.  Whatever you put here will be included as the
copyright holder in the header of the po files.

- MSGID_BUGS_ADDRESS
Usually your name and e-mail address.  It will also be included in the
po header and translators will check this entry when they come across
a bug in a msgid, or when they have difficulties to translate a certain
message because of awkward coding on your side.

Okay, after "make pot" we have updated the master message catalog
TEXTDOMAIN.pot, in our case "org.imperia.simplecal.pot".  Have a look
into the file now.  It contains the original English messages that
xgettext has extracted from our source files and blank translations.
The po files (the files the names of which end with ".po") contain
previous translations provided by our package translators.  Whenever
you change the Perl sources, the list of messages may change.  This
results in a maybe new .pot file and requires an update of all po
files.  Try that now and type "make update-po"

You will see confusing output from "make" but you may get the idea
that every single po file (every language that the package supports)
gets updated, and the new strings are inserted into the po
files. Since nothing really changed here (we did not change the source
files yet) you can now try to update the compiled po files which end in
".mo" with "make update-mo".

Again, you will see maybe cryptic output from "make" that signifies
that all compiled files are re-generated now by a program called
"msgfmt".

The last step requires that you copy the (possibly changed) mo files
into your package by "make install".  This will copy the mo files into
the subdirectory "LocaleData" of your package so that libintl-perl is
able to find them at runtime.

You can perform all these steps at once by typing "make all" although
this is mostly useful for testing purposes.  In reality the workflow
is different:

- You change your source files, messages may have been added, deleted
  or modified.  You will have to update the master message catalog by
  typing "make pot".

- Since the translations may have gotten out-of-date, you will have to
  merge your changes into all po files by "make update-po".

- Your translators will get copies of the po files, reflect your
  changes in the po files and send them back to you.

- When you have received the updates, it is time to compile the po
  files into a binary representation with "make update-mo".

- These binary mo files have to be installed under "LocaleData", and
  you have to "make install".  Note that "make install" installs the
  mo files in your source package, not in the system location!

- Now that you have updated the translations for your package, you
  will want to upload a new version to the CPAN.

Note that all these steps are *only* necessary for package
maintainers.  As a user of the package, you will only see the
resulting mo files under "LocaleData".  End users do *not* need any of
the gettext tools, and they do not have to perform any of the above
steps theirselves!

Changing the Sources
--------------------

You may wonder whether your translators have to re-translate
everything from scratch whenever you change your Perl sources.  This
is, of course, not the case.  Let's say, you want to add a welcome and
a good-bye message to the program output.  Have a look into
"bin/simplecal.pl" and you will see that this is already prepared but
commented out (search for "Welcome to" and "Bye" if you can't find
it).  Uncomment these lines and see what happens to the po files in
that case.

Before you proceed, you should have a look at the Dutch translation
file "nl.po".  At the bottom you will find some lines that are
commented out with "#~" and that proove that I have already prepared
that case.  The comment sign "#~" in po files signifies that a
particular translation is obsoleted, i. e. no longer needed because it
is no longer present in the source files.

Say, that you have really changed your mind, and you want to
re-introduce the welcome and good-bye messages to your program and you
uncomment the corresponding lines in "bin/simplecal.pl".  You will
have to re-make the master catalog "org.imperia.simplecal.pot" by
"make pot", and then "make update-po" to update the po files.  In
fact, "make update-po" is sufficient because it will also update the
pot file if it is out-of-date (i. e. if any of the source files have
changed in the meantime).

Type "make update-po" now, and look again at "po/nl.po".  You will see
that the previously translated welcome and good-bye messages have been
re-activated from the obsoleted entries.  In fact your translators
will have nothing to do, because their old translations are still
valid.  Type "make install" and then re-run "perl -Ilib
bin/simplecal.pl", set the environment variable "LANG" to any of the
available languages, and things will still work perfectly.

Of course, it is a rare case that messages are discarded and later
re-activated in programming sources.  It is more likely that you will
modify a message, or maybe add a message that is similar to former
ones.  Let's say that you want to change the exclamation mark in the
good-bye message at the bottom of the script to a simple full stop.
Look for the line that reads

     print __"Bye!\n";

and change it into

     print __"Bye.\n";

Change into the directory "po", update the translation files with
"make update-po" and inspect the file "nl.po".  At first glance, you
may not see any change.  But then: The entry for the good-bye message
has an additional comment "#, fuzzy".  The fuzzy mark signifies that
the msgerge program has found that a message is very similar to a
previous message (even obsoleted ones are taken into account), and
that it proposes an old translation here.  The translator will
normally modify the translation accordingly (without having to re-type
everything), remove the fuzzy mark and send back the translation to
you.

In fact you could also install translations that have not been revised
by the translator and are still marked as fuzzy.  This is not
recommended however! The algorithm used in msgmerge is quite smart
and seldom fails to detect minimal changes in the source message and
propose the old translation.  However, it often proposes translations
from other valid or obsoleted entries that are only vaguely related to
the real meaning.  You should understand the fuzzy merging mechanism
as a helpful feature to the translator only and never install fuzzy
translations unless you absolutely know what you are doing.

Pass Comments to Translators
----------------------------

The po files contain references for every message to the corresponding
source files as comments.  But you still may feel a need for giving
hints to the translators.  You may want to tell the translators, that
the good-bye message can be somewhat sloppy (or whatever you like).
This is simple to do.  Have a look at the good-bye message in
"bin/simplecal.pl" and you will see that it is preceded by a comment
introduced with the string "TRANSLATORS:".  If you start your Perl
comment like this, it will end up as a comment for translators in the
resulting po (resp. pot) file and may serve as a hint for translators.

In fact, the string "TRANSLATORS:" is arbitrarily chosen.  If you
prefer another string, change it in the invocation of "xgettext" in
the skeleton Makefile provided here.

Informational Files
-------------------

You should put two additional files in your distribution.  The first
one is "README-NLS".  It should be a verbatim copy of the most recent
version found in the "simplecal" sample package.  Please send
corrections or improvements to this file to the maintainer Guido
Flohr <guido@imperia.net>, and add package-specific notes to your
documentation instead.  Users expect this file to have a standard
contents, and they will not check it for changes on a regular basis.

The file "TRANSLATIONS" should reflect the current translation status
of your package.  It should list all currently availabe translations,
their completeness, and it should also inform your user which translations
are actively maintained, and which are not.  You can find a sample
in the "simplecal" sample package.

Bringing It All Together
------------------------

The above sounds definitely more complicated than it is.  In practice
you code as before but mark all your strings with "__" and friends
like described in the POD of Locale::TextDomain.  Before a new release
you change into the directory "po" of your distribution and type "make
update-po" to update the available translations.  Distribute the
modified po files to your translators, and once you have collected
them all, type "make install" to add them to your distribution.

That's all, all translations will be available in your package now.

Internationalizing Existing Packages
------------------------------------

Internationalizing an already existing package with libintl-perl is
less painful than you think.  The following roadmap should do it with
minimal effort.

First create a subdirectory "po" in your sources, copy the "Makefile"
from this sample, and copy and edit the files "TEXTDOMAIN" and
"LINGUAS" (LINGUAS can set the Makefile variable "LINGUAS" to the
empty string and TEXTDOMAIN should set "TEXTDOMAIN" to a name as
advised in the POD of Locale::TextDomain).

Next you have to mark the translatable strings in your sources with
"__" and friends.  You can do that by hand, but isn't that the kind of
job that you have bought a computer for?  List your source files in
"po/POTFILES.in" and then try

     xgettext -a --files-from=POTFILES.in -o all.pot

The option "-a" instructs xgettext to extract *all* strings from your
sources.  This option may miss a few strings (consider a bug report in
that case), it will issue a lot of warnings about "illegal variable
interpolations" (see the POD of Locale::TextDomain for workarounds)
and will put a lot of strings extracted from your sources into the
file "all.pot".

Now, load the file "all.pot" into an editor of your choice.  If your
choice is "GNU emacs" you will have maximum comfort: Select an entry,
type "s" and you can cycle through the source files that this
particular entry originates from.  Other PO editors like KBabel or
PO-Edit provide similar functionality.  But even with the "Notepad" on
MS-DOS you will be able to navigate to the corresponding source file.
Once you have found the origin in your sources, you have to decide
whether this is a false positive, and you simply ignore it.  If it is
a translatable string you either simply mark it with "__" or you
"repair" it.

What does "repair" mean? Again, the POD of Locale::TextDomain... In
brief: Your Perl sources will be full of stuff like:

     die "Cannot open file '$filename': $!\n";

This string is not suitable for translation, because it is not
constant.  It may change depending on the value of the variable
$filename and the value of $!.  You will have to change that into
something like:

     die __x ("Cannot open file '{filename}': {err}\n",
              filename => $filename, err => $err);

Once you are done with marking the strings, you can try to run your
scripts/modules and you will see a lot of complaints by Perl that it
doesn't know about "__" (in various incarnations).  Remember that "__"
is really a function call and you have to import the function "__" and
its relatives into your namespace.

What you have to do is to invent an identifier for your package (see
Locale::TextDomain for hints) and then add the following line to all
of your source files that produced errors:

     use Locale::TextDomain ('Name-Of-My-Package');

You will be happy if "Name-Of-My-Package" is the same as the Makefile
variable "TEXTDOMAIN" in the file "po/TEXTDOMAIN" that you have
created in the beginning.

For the common case of a pure library: Is that really all I have to
do? Yes! What about POSIX::setlocale(), don't I have to make a call
somewhere? No, not for a library! And what about calls to textdomain()
and bindtextdomain() that I know from C or other languages? No, this
is all hidden in "use TextDomain (PACKAGENAME)" for Perl.

To make it clear again: A library should NEVER change the locale
settings.  The script that uses a library (or multiple libraries)
should do that, and this boils down to three lines of Perl:

     use POSIX qw (setlocale);
     use Locale::Messages (LC_MESSAGES);

     setlocale (LC_MESSAGES, "");

That means: The *calling* Perl script, the one that uses possibly
internationalized libraries, should initialize the locale settings to
the user preferences.  Libraries should honor that setting but should
never change it.  If a script misses a call to setlocale(), your
internationalized library will happily continue to work flawlessly
with the original English messages, it is up to the client programmer
to reveal the i18n features in your code!

If you are new to internationalization (i + 18 characters + n = i18n),
you will probably only understand half of the above.  Visit
http://ml.imperia.org/listinfo/libintl-perl/, subscribe to the mailing
list libintl-perl@imperia.net and ask there.  And don't blame me, the
author, for any difficulties.  libintl-perl is as complicated as i18n
itself, it even simplifies a lot of things.  The complicated rest is
inevitable. ;-)

Good luck!

Guido


code:
MANIFEST
Makefile.PL
bin/simplecal.pl
lib/SimpleCal.pm
po/Makefile
po/PACKAGE
po/POTFILES.in
po/ar.po
po/ar_SA.po
po/de.po
po/de_AT.po
po/fr.po
po/ga.po
po/it.po
po/nl.po
po/org.imperia.simplecal.pot
po/pt.po
po/pt_BR.po
po/ru.po
lib/LocaleData/de/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/fr/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/ar/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/ar_SA/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/de_AT/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/ga/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/it/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/pt/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/pt_BR/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/nl/LC_MESSAGES/org.imperia.simplecal.mo
lib/LocaleData/ru/LC_MESSAGES/org.imperia.simplecal.mo


code:
#! /usr/local/bin/perl -w # -*- perl -*-

# vim: tabstop=4
# $Id: Makefile.PL,v 1.1 2009-11-01 19:00:24 pertusus Exp $

# Makefile generator for libintl-perl.
# Copyright (C) 2002-2009 Guido Flohr <guido@imperia.net>,
# all rights reserved.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.

# You should have received a copy of the GNU Library General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.

use ExtUtils::MakeMaker;

# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.

WriteMakefile(
    NAME		    => 'simplecal',
    VERSION_FROM	=> 'bin/simplecal.pl',
    PREREQ_PM	    => {
							Locale::TextDomain => 0,
						},
	EXE_FILES		=> [ 'bin/simplecal.pl' ],
	AUTHOR          => 'Guido Flohr <guido@imperia.net>',
    ABSTRACT        => 'Demonstration package for libintl-perl',
);

__END__

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
cperl-indent-level: 4
cperl-continued-statement-offset: 2
tab-width: 4
End:


code:
Notes on National Language Support (NLS)
****************************************

This package is internationalized with libintl-perl, a free
internationalization library for Perl, you will need to install a copy of
libintl-perl in order to use the package.  You can get libintl-perl from the
Comprehensive Perl Archive Network CPAN at http://www.cpan.org/.

The following notes are meant to be a quick start guide for somewhat
experienced users and system administrators and many important details had to
be omitted for brevity.  If you have any difficulties with the
internationalization features of this package, no matter if you are a
programmer, a translator, or an end user, feel free to ask at the mailing list
for libintl-perl.  To do so, send an e-mail to the address
<libintl-perl AT imperia DOT net> (please replace "AT" with a "@", and "DOT"
with a dot ".").

You can subscribe to this list at

     http://ml.imperia.org/mailman/listinfo/libintl-perl

A searchable archive of earlier postings is located at

     http://ml.imperia.org/libintl-perl/

You may already find an answer to your question there.

Feel free to include this document in your own Perl packages internationalized
with libintl-perl, no severe copyright restrictions apply.  You should send
corrections or improvements to the author Guido Flohr <guido AT imperia DOT
net>, so that others can benefit from your changes.

The End User's View
===================

The installation routine for this package will automatically take care that
your system has a sufficient version of libintl-perl installed.  This is
basically sufficient for proper operation, but - especially if
internationalized software is new to you - you should read on carefully in
order to fully benefit from the internationalization (I18N) features of this
package.

Perl Setup
----------

The I18N library libintl-perl will run with a wide range of Perl versions (at
least from Perl version 5.005_03 to Perl 5.8.0) but you will experience slight
difference in features and performance depending on the version of Perl you
use.

With Perl versions prior to 5.7.3 you can use the package for all European
scripts (including those with Greek or Cyrillic scripts), and also for many
scripts used outside Europe, like Arabic, Hebrew, Georgian, Vietnamese or
Thai, more general all scripts using 8 bit charsets.  Other scripts are only
available if the translations in this package are provided in Unicode and they
can only be output in Unicode.

Beginning with Perl 5.7.3 the module Encode became part of the Perl core, and
it offers you a much wider range of possible scripts.  If you plan to use some
of the lesser used scripts for Chinese, Japanese, and Korean, you should also
install the module Encode::HanExtra.

Setting Your Language
---------------------

Most modern systems are already prepared and configured for
internationalization, and the user interface of the software you have
installed will already be configured for your preferred language.  Packages
internationalized with libintl-perl will honor these configuration settings
and will also operate in your preferred language if the necessary translations
are available.

The environment variable "LANGUAGE" has the highest precedence for
translations.  The most common format for this environment variable is a
(lowercase) two-letter language code and an (uppercase) two-letter country
code separated by an underscore "_", for example:

     LANGUAGE=fr_BE
     export LANGUAGE

This will set your language preferences to French ("fr") for Belgium ("BE").
Other examples are French for France ("fr_FR"), German for Austria ("de_AT"),
and so on.  You can also omit the country part ("FR", "DE", "IT", "RU", ...)
in which case a default setting for the country will be assumed.

If there are no translations available for your selected languages, the
original message (normally in English) will be displayed.

You can also define a chain of languages to be tried separated by a colon:

     LANGUAGE=fr_BE:fr_FR:fr:it

Read this as: "I want translations in French for Belgium.  If they are not
available try French for France, then any French translation, and finally
Italian".  Please note that this chain notation is only allowed for the
environment variable "LANGUAGE", it is not valid for any of the following
variables.

If "LANGUAGE" is not set, the library checks the variable "LANG".  It has the
same syntax as "LANGUAGE" but does not allow the preferences chain with the
colon syntax.  After "LANG" the variable "LC_MESSAGES" (think "locale category
messages") is tried, and finally "LC_ALL".

Note for Microsoft Windows users: The locale preferences you have configured
for your system cannot yet be evaluated by libintl-perl.  This may change for
future versions of libintl-perl but for the moment you have to make do with
the instructions given above.  In order to set environment variables, you have
to right-click on the icon "My Computer" on your desktop, select "Properties"
in the context menu, and then click the tab labelled "Environment variables".

Setting the Output Charset
--------------------------

Even if you have managed to properly select your preferred language, you may
still have difficulties reading the program languages, because libintl-perl
was unable to determine the correct charset to use for messages.  For example,
it may assume Unicode ("UTF-8") but you really need ISO-Latin-1 (also known as
"Latin-1" or "ISO-8859-1").  If this is the case, please set the environment
variable "OUTPUT_CHARSET" to the appropriate value, for example:

     OUTPUT_CHARSET=iso-8859-1
     export OUTPUT_CHARSET

Charset names are case-insensitive, i. e. "LATIN-1" is the equivalent to
"Latin-1" or even "lAtIn-1".

Note: The output charset "utf8" is NOT recognized.  Please use the correct
abbreviation "utf-8" (with a hyphen) instead.

The Translator's View
=====================

If you want to contribute a new translation to this package, please contact
the author first.  Somebody may have already started this translation, and
furthermore the package author will be able to give you detailled instructions
and help.

Translating a Perl package is not much work and it does not require any
technical skills.  If you are able to use the software itself, you will also
be able to contribute a translation for your language.  But why should you do
that? You are able to read and understand this text and you will also be able
to understand the English messages that the software spits out by default.

Computers are an integral part of today's society.  Computers are used to
explore new sources of information, forbidding computers would be a modern
form of censorship.  Computers may also improve social life, the internet
helps people to find contacts in their area and all over the world, even if
they would otherwise be deprived from that because of a handicap, lack of money
for traveling, or other reasons.  In many societies, the ability to use and
handle a computer also has a strong impact on your perspectives in life, you
may not be able to find an adequate job because of your lack of computer
experience, or you may even lose your job because of that.

Everybody should benefit from computers, regardless of cultural
background. Computers are expansive goods, and their price is already a high
barrier to cross.  If computers speak in a foreign language, the learning
curve gets steeper and the barrier gets even higher.  You can help the people
that share your native language by contributing a translation.  The author of
this package has already prepared everything, the rest is up to you!

The Programmer's View
=====================

You have downloaded this package because you want to use it in your own
project(s).  The fact that the package is internationalized with libintl-perl
does not affect its usability in any way.  But you should keep in mind that
textual messages produced by the package may change according to the locale
settings at run-time.  This can lead to errors.  For example, if you parse
error messages produced by the package, you will most probably fail to detect
what you are looking for, if these error messages are suddenly presented in
another language or another output charset.

It is probably needless to say that this is bad practice and an indicator for
a poorly written interface.  Either you have missed the correct method for
determining the substance of the message in a locale-independent manner, or
the author of the package has mis-designed the package interface.  In any
case, this is a technical problem that should be solved by technicians.  You
should not put that burden on the shoulders of your users but rather solve the
problem in cooperation with the author of the module that causes it.

If this is absolutely impossible, as a temporary workaround you can completely
switch off the native language support of the package by setting the
environment variable "LANGUAGE" to the special value "C":

     BEGIN {
         $ENV{LANGUAGE} = $ENV{LANG} = "C";
     }

The value "C" instructs libintl-perl to leave all messages untouched, and you
can use the package as if it was not internationalized at all.

If the project you are working on is not yet internationalized, you should
consider to prepare it for internationalization now.  Doing so is only little
work for yourself, but results in a large benefit for the users of your
software.  The package "libintl-perl" ships with exhaustive documentation for
programmers and a sample package that you can use as a skeleton for your own
project(s).  Internationalizing Perl software with libintl-perl is easy, the
package that this file is a part of, prooves that.

Guido Flohr


code:
This package is internationalized with libintl-perl.  It can be configured
to speak in different languages.  If your language is still missing, you
should take a look into the file "README-NLS" (section "The Translator's
View") where you will find instructions for contributing a translation
into your language to this package.

You can see the translation status of this package for various languages
in the following table.  If your language is completely missing in the
table, your translation will be welcomed as a contribution to the
project.

Translation Status for "simplecal"
==================================

Language            | Code  | Status | Current Translator
--------------------+-------+---+----+---------------------------------
Arabic              | ar    | O | C  | Guido Flohr
Arabic/Saudi Arabia | ar_SA | O | C  | Guido Flohr
Dutch               | nl    | O | C  | Guido Flohr
French              | fr    | O | C  | Guido Flohr
German              | de    | A | C  | Guido Flohr
German/Austria      | de_AT | O | C  | Guido Flohr
Irish               | ga    | O | C  | Guido Flohr
Italian             | it    | O | C  | Guido Flohr
Portuguese          | pt    | O | C  | Guido Flohr
Portuguese/Brazil   | pt_BR | O | C  | Guido Flohr
Russian             | ru    | O | C  | Guido Flohr

Legend: The status is displayed in two columns.  The left column informs
you whether somebody is already in charge of that translation, the right
columns reflects the status of the translation itself.

Left:  (O) - Orphaned:  The translation needs a new maintainer.  You?
       (A) - Assigned:  The translation already has a maintainer.

Right: (C) - Complete:  All messages are translated.
       (M) - Mostly:    Most (all important) messages are translated.
       (P) - Partially: The translation is only partially done.
       (-) - Missing:   No translation is currently available.

Messages for missing translations will be displayed in English.


code:
#! /usr/local/bin/perl -w
# vim: tabstop=4

use strict;

# Include our little library.
use SimpleCal;

# For setlocale.
use POSIX qw (setlocale);
use Locale::Messages qw (LC_MESSAGES);

# Our script contains translatable messages.  We have to assign
# it a text domain.  Note that this is only needed here because the
# script *itself* contains translatable messages from the text domain
# "org.imperia.simplecal".
use Locale::TextDomain ('org.imperia.simplecal');

use vars qw ($VERSION);
$VERSION = '1.0';

# Set the locale according to the environment.
setlocale (LC_MESSAGES, "");

# Print a greeting message.  We want to be flexible with the package
# name, and so we will make this a placeholder.
my $package_name = "SimpleCal";
#print __x("Welcome to {package}!\n", package => $package_name);

# Inquire current date and time.
my @now = localtime;
my $year = $now[5] + 1900; # Current year.
my $month = $now[4];    # Current month in the range of 0-11.

# Print the header for our calendar.
my $month_name = SimpleCal::month_name ($month);
print "\t$month_name $year\n";

# And now print the abbreviation for every day of the week.
foreach my $i (0 .. 6) {
	# This makes the (insecure!) assumption that the abbreviated
	# week day is no longer than 5 characters.
	printf "%6s", abbr_week_day ($i);
}
# And a final newline.
print "\n";

# The rest of the program only prints out the day numbers and is not
# particularly interesting.

# We will start at a Sunday where month day <= 0 and suppress negative dates
# later.
my $first_day = $now[3] - $now[6];
if ($first_day > 0) {
	$first_day %= 7;
	$first_day -= 7;
}

my $last_day;
if ($month == 1) {
	if (SimpleCal::is_leap_year ($year)) {
		$last_day = 29;
	} else {
		$last_day = 28;
	}
} elsif ($month == 3 || $month == 5 || $month == 8 || $month == 10) {
	$last_day = 30;
} else {
	$last_day = 31;
}

my $day_of_week = 0; # Sunday.
foreach my $mday ($first_day .. $last_day) {
	if ($mday <= 0) {
		printf "%6s", ' ';
	} else {
		printf "% 6d", $mday;
	}
	++$day_of_week;
	if ($day_of_week == 7) {
		$day_of_week = 0;
		print "\n";
	}
}

print "\n" if $day_of_week;

# Say good bye.
# TRANSLATORS: This may be a colloquial way of saying good bye to the user.
#print __"Bye.\n";

__END__

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
cperl-indent-level: 4
cperl-continued-statement-offset: 2
tab-width: 4
End:


code:
#! /bin/false
# vim: tabstop=4

package SimpleCal;

use strict;

# The text domain (identifier) of our package is 'org.imperia.simplecal',
# following the advice in the pod of Locale::TextDomain.
use Locale::TextDomain qw (org.imperia.simplecal);

use base qw (Exporter);
use vars qw (@EXPORT);

@EXPORT = qw (month_name abbr_week_day is_leap_year);

sub month_name ($)
{
	my $month = shift;
	$month = 0 unless $month;
	$month %= 12;

	# This is of course stupid but explains things best.  See the
	# function abbr_week_day() for a smarter approach.
	if ($month == 0) {
		return __"January";
	} elsif  ($month == 1) {
		return __"February";
	} elsif  ($month == 2) {
		return __"March";
	} elsif  ($month == 3) {
		return __"April";
	} elsif  ($month == 4) {
		return __"May";
	} elsif  ($month == 5) {
		return __"June";
	} elsif  ($month == 6) {
		return __"July";
	} elsif  ($month == 7) {
		return __"August";
	} elsif  ($month == 8) {
		return __"September";
	} elsif  ($month == 9) {
		return __"October";
	} elsif  ($month == 10) {
		return __"November";
	} else {
		return __"December";
	}
}

# This is smarter.  We initialize an array with the English names first.
# The function N__() is exported by Locale::TextDomain and returns
# its argument unmodified.  Its sole purpose is to mark the string as
# being translatable, so that it will make it into the pot file for
# our package.
#
# It is dangerous to use __() here! Why? Then the array will be translated
# only once, at compile time.  It is very likely that the locale settings
# have not yet been initialized to the user preferences at this point
# of time, and since the array is already created, the translation
# will not produce the correct results.
#
# This should become clearer if you imagine that our library would be
# part of a daemon that is running for days or even years.  The array
# would be initialized with the language that was set at program startup
# and would then never change again, because you actually cache the
# translations.
my @abbr_week_days = (
	N__"Sun",
	N__"Mon",
	N__"Tue",
	N__"Wed",
	N__"Thu",
	N__"Fri",
	N__"Sat",
);

sub abbr_week_day ($)
{
	my $wday = shift;
	$wday = 0 unless $wday;
	$wday %= 7;

	# The argument to __() is simply a string, not necessarily a string
	# constant.  The following line will look up the English name in the
	# array, and then translates that string on the fly into the current
	# user language.
	return __($abbr_week_days[$wday]);
	# This can still be suboptimal because it translates the string again
	# and again.  In situations where you are absolutely sure that the
	# user language will not change again, you may prefer to cache the
	# translations despite of the above hints, especially if you
	# call the function very many times.  In a library you can usually
	# not be sure whether the user language can change or not and you
	# should avoid that.  The message lookup is quite fast.

	# Instead of the above return directive we could also have written:
	#
	#   return $__{$abbr_week_days[$wday]};
    #
    # resp.
    #
    #   return $__->{$abbr_week_days[$wday]};
    #
    # It is basically a matter of taste whether you prefer the tied
	# hash lookup or the function call.
}

# Check whether the argument is a leap year.
sub is_leap_year
{
	my $year = shift;
	$year = 0 unless $year;

	return 1 if $year % 4 == 0 && ($year % 100 != 0 || $year % 400 == 0);

	return;
}

1;

__END__

Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
cperl-indent-level: 4
cperl-continued-statement-offset: 2
tab-width: 4
End:


code:
# Makefile for various po files.

srcdir = .
libdir = ../lib

#CATALOGS = $(addsuffix .po, LINGUAS)
CATALOGS = $(LINGUAS)
MO_FILES = $(addsuffix .mo, $(LINGUAS))

MSGMERGE = msgmerge
MSGFMT   = msgfmt
XGETTEXT = xgettext
CATOBJEXT = .po

include PACKAGE

TD = $(strip $(TEXTDOMAIN))

default: help

all: $(TD).pot update-po update-mo install

help:
	@echo "Available targets:"
	@echo "  pot                       - remake master catalog"
	@echo "  update-po                 - merge po files"
	@echo "  update-mo                 - regenerate mo files"
	@echo "  install                   - install mo files"
	@echo "  all			   - all of the above"

POTFILES = $(srcdir)/POTFILES.in \
	$(shell cat $(srcdir)/POTFILES.in)

pot: $(TD).pot

clean:
	rm -f *~ *.bak *.mo

# FIXME: The parameter --from-code is only needed if your sources contain
# any 8 bit data (even in comments).  UTF-8 is only a guess here, but it
# will at least accept any 8 bit data.
#
# The parameter "--language=perl" is not strictly needed because the
# source language of all our files will be auto-detected by xgettext
# by their filename extension.  You should even avoid this parameter
# if you want to extract strings from multiple source languages.
$(TD).pot: $(POTFILES)
	$(XGETTEXT) --output=$(srcdir)/$(TD).pox --from-code=utf-8 \
		--add-comments=TRANSLATORS: --files-from=$(srcdir)/POTFILES.in \
		--copyright-holder="$(COPYRIGHT_HOLDER)" \
		--msgid-bugs-address="$(MSGID_BUGS_ADDRESS)" \
		--keyword --keyword='$$__' --keyword=__ --keyword=__x \
		--keyword=__n:1,2 --keyword=__nx:1,2 --keyword=__xn:1,2 \
		--keyword=__p:1c,2 --keyword=__np:1c,2,3 \
		--keyword=__npx:1c,2,3 --keyword=N__ --keyword=N__n:1,2 \
		--keyword=N__p:1c,2 --keyword=N__np:1c,2,3 --keyword=%__ \
		--language=perl && \
	rm -f $@ && mv $(TD).pox $@

install: $(MO_FILES)
	cd $(srcdir); \
	targetdir='$(libdir)/LocaleData'; \
	languages='$(LINGUAS)'; \
	for lang in $$languages; do \
		mkdir -p "$$targetdir/$$lang/LC_MESSAGES" || exit 1; \
		dest="$$targetdir/$$lang/LC_MESSAGES/$(TD).mo"; \
		cat="$$lang.mo"; \
		echo "installing $$cat as $$dest"; \
		cp -f $$cat $$dest && chmod 644 $$dest || exit 1; \
	done

update-mo: $(MO_FILES)

update-po:
	$(MAKE) $(TD).pot
	cd $(srcdir); \
        catalogs='$(CATALOGS)'; \
        for cat in $$catalogs; do \
          cat=`basename $$cat`; \
          lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \
          mv $$lang.po $$lang.old.po; \
          echo "$$lang:"; \
          if $(MSGMERGE) $$lang.old.po $(TD).pot -o $$lang.po; then \
            rm -f $$lang.old.po; \
          else \
            echo "msgmerge for $$cat failed!"; \
            rm -f $$lang.po; \
            mv $$lang.old.po $$lang.po; \
          fi; \
        done

.SUFFIXES:
.SUFFIXES: .po .mo

.po.mo:
	$(MSGFMT) --check --statistics --verbose -o $@ $<


code:
# Makefile snippet that holds all package-dependent information.

# Add more languages here!  Beware that this is a makefile snippet and
# you have to adhere to make syntax.
LINGUAS = de \
	  de_AT \
	  it \
	  ru \
	  fr \
	  ar \
	  ar_SA \
	  ga \
	  pt \
	  pt_BR \
	  nl \

# Textdomain for our package.
TEXTDOMAIN = org.imperia.simplecal

# Initial copyright holder added to pot and po files.
#COPYRIGHT_HOLDER = Guido Flohr
COPYRIGHT_HOLDER = Edit the file PACKAGE to change this.

# Where to send msgid bugs?
#MSGID_BUGS_ADDRESS = Guido Flohr <guido@imperia.net>
MSGID_BUGS_ADDRESS = Edit the file PACAKGE to change this.


code:
../bin/simplecal.pl
../lib/SimpleCal.pm


code:
# Arabic translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-27 18:56+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: Arabic <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-6\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "�����"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "������"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "����"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "�����"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "����"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "�����"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "�����"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "�����"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "������"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "������"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "������"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "������"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "�"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "�"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "�"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "�"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "�"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "�"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "�"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "���� �� {package} ��\n"

#~ msgid "Bye.\n"
#~ msgstr "!��� ������\n"


code:
# Arabic translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-27 18:56+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: Arabic <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-6\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "����� ������"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "����"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "����"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "�����"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "������"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "����"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "����"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "��"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "�����"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "����� �����"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "����� ������"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "����� �����"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr ""

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr ""

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr ""

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr ""

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr ""

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr ""

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr ""


code:
# German translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: Edit the file PACAKGE to change this.\n"
"POT-Creation-Date: 2005-08-16 18:42+0300\n"
"PO-Revision-Date: 2003-07-28 04:06+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: German <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "Januar"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "Februar"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "M�rz"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "April"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "Mai"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "Juni"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "Juli"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "August"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "September"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "Oktober"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "November"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "Dezember"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "So"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "Mo"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "Di"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "Mi"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "Do"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "Fr"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "Sa"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "Willkommen bei {package}!\n"

#~ msgid "Bye.\n"
#~ msgstr "Tsch�ss!\n"


code:
# German (Austria) translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-27 18:47+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: German <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "J�nner"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "Feber"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr ""

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr ""

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr ""

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr ""

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr ""

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr ""

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr ""

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr ""

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr ""

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr ""

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr ""

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr ""

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr ""

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr ""

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr ""

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr ""

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr ""


code:
# French translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-28 04:07+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: French <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "janvier"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "f�vrier"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "mars"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "avril"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "mai"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "juin"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "juillet"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "ao�t"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "septembre"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "octobre"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "novembre"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "d�cembre"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "di"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "lu"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "ma"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "me"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "je"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "ve"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "sa"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "Bienvenu � {package}!\n"

#~ msgid "Bye.\n"
#~ msgstr "� toute � l'heure!\n"


code:
# Irish translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-28 04:10+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: Irish <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "Ean�ir"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "Feabhra"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "M�rta"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "Aibre�n"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "M� na Bealtaine"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "Meith"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "I�il"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "L�nasa"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "Me�n F�mhair"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "Deireadh F�mhair"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "M� na Samhna"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "M� na Nollag"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "Domh"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "Luan"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "M�irt"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "C�ad"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "D�ar"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "Aoine"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "Sath"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "B�idh {package} agat agus f�ilte!\n"

#~ msgid "Bye.\n"
#~ msgstr "Sl�n leat!\n"


code:
# Italian translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-28 04:10+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: Italian <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "gennaio"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "febbraio"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "marzo"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "aprile"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "maggio"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "giugno"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "luglio"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "agosto"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "settembre"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "ottobre"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "novembre"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "dicembre"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "dom"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "lun"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "mar"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "mer"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "gio"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "ven"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "sab"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "Benvenuti in �{package}!\n"

#~ msgid "Bye.\n"
#~ msgstr "Ciao!\n"


code:
# Dutch translations for SimpleCal.
# Copyright (C) 2003 Imperia AG, Guido Flohr <guido@imperia.net>.
# This file is distributed under the same license as libintl-perl.
#
msgid ""
msgstr ""
"Project-Id-Version: SimpleCal\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2003-07-28 04:27+0200\n"
"PO-Revision-Date: 2003-07-28 04:08+0200\n"
"Last-Translator: Guido Flohr <Guido.Flohr@imperia.net>\n"
"Language-Team: Dutch <guido@imperia.net>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr "januari"

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr "februari"

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr "maart"

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr "april"

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr "mei"

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr "juni"

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr "juli"

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr "augustus"

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr "september"

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr "oktober"

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr "november"

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr "december"

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr "zo"

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr "ma"

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr "di"

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr "wo"

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr "do"

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr "vr"

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr "za"

#~ msgid "Welcome to {package}!\n"
#~ msgstr "Welkom bij {package}!\n"

#~ msgid "Bye.\n"
#~ msgstr "Tot zo!\n"


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR Edit the file PACKAGE to change this.
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: Edit the file PACAKGE to change this.\n"
"POT-Creation-Date: 2005-08-17 11:53+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: ../lib/SimpleCal.pm:26
msgid "January"
msgstr ""

#: ../lib/SimpleCal.pm:28
msgid "February"
msgstr ""

#: ../lib/SimpleCal.pm:30
msgid "March"
msgstr ""

#: ../lib/SimpleCal.pm:32
msgid "April"
msgstr ""

#: ../lib/SimpleCal.pm:34
msgid "May"
msgstr ""

#: ../lib/SimpleCal.pm:36
msgid "June"
msgstr ""

#: ../lib/SimpleCal.pm:38
msgid "July"
msgstr ""

#: ../lib/SimpleCal.pm:40
msgid "August"
msgstr ""

#: ../lib/SimpleCal.pm:42
msgid "September"
msgstr ""

#: ../lib/SimpleCal.pm:44
msgid "October"
msgstr ""

#: ../lib/SimpleCal.pm:46
msgid "November"
msgstr ""

#: ../lib/SimpleCal.pm:48
msgid "December"
msgstr ""

#: ../lib/SimpleCal.pm:70
msgid "Sun"
msgstr ""

#: ../lib/SimpleCal.pm:71
msgid "Mon"
msgstr ""

#: ../lib/SimpleCal.pm:72
msgid "Tue"
msgstr ""

#: ../lib/SimpleCal.pm:73
msgid "Wed"
msgstr ""

#: ../lib/SimpleCal.pm:74
msgid "Thu"
msgstr ""

#: ../lib/SimpleCal.pm:75
msgid "Fri"
msgstr ""

#: ../lib/SimpleCal.pm:76
msgid "Sat"
msgstr ""