Log-Report-Lexicon-1.12/0000755000175000001440000000000015000465541015544 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/t/0000755000175000001440000000000015000465541016007 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/t/22compact.t0000644000175000001440000000320414775703615020004 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Lexicon POTcompact # Structure of parsed result has also been checked manually, using # Data::Dumper (MO 2007/05/11) use warnings; use strict; use lib 'lib', '../lib'; use utf8; use Test::More tests => 21; use File::Basename qw/dirname/; use File::Spec::Functions qw/catfile/; use_ok('Log::Report::Lexicon::POTcompact'); my $sl_po = catfile(dirname(__FILE__), 'hello-world-slovak.po'); # # Try reading complex example # slightly modified from gettext examples in slovak # my $pot = Log::Report::Lexicon::POTcompact->read($sl_po, charset => 'utf-8'); ok(defined $pot, "read pot file"); isa_ok($pot, 'Log::Report::Lexicon::POTcompact'); # # header # is($pot->header('mime-version'), '1.0', 'access to header'); # # extended single case # my $po = $pot->msgid('Hello, world!'); ok(defined $po, "got greeting"); ok(!ref $po, "one translation only"); is($po, "Pozdravljen, svet!"); is($pot->msgstr("Hello, world!"), "Pozdravljen, svet!"); is($pot->msgstr("Hello, world!", 0), "Pozdravljen, svet!"); is($pot->msgstr("Hello, world!", 5), "Pozdravljen, svet!"); # # with plurals # is($pot->msgstr('Aap', 0), 'A', 'msgstr by plural'); is($pot->msgstr('Aap', 1), 'B'); is($pot->msgstr('Aap', 2), 'C'); is($pot->msgstr('Aap', 3), 'D'); is($pot->msgstr('Aap', 4), 'D'); is($pot->msgstr('Aap', 5), 'A'); is($pot->msgstr('Aap', 6), 'A'); is($pot->msgstr('Aap', 100), 'A'); is($pot->msgstr('Aap', 101), 'B'); # # with multi-lines and utf # my $po2 = $pot->msgid("This program is running as process number {pid}.multi-line\n"); ok(defined $po2, 'test multi'); is($po2, "Ta program teče kot proces številka {pid}.multi\tline\n"); Log-Report-Lexicon-1.12/t/simplecal/0000755000175000001440000000000015000465541017760 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/t/simplecal/ga.po0000644000175000001440000000337214775703615020733 0ustar00markovusers00000000000000# Irish translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Irish \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 "Eanir" #: ../lib/SimpleCal.pm:28 msgid "February" msgstr "Feabhra" #: ../lib/SimpleCal.pm:30 msgid "March" msgstr "Mrta" #: ../lib/SimpleCal.pm:32 msgid "April" msgstr "Aibren" #: ../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 "Iil" #: ../lib/SimpleCal.pm:40 msgid "August" msgstr "Lnasa" #: ../lib/SimpleCal.pm:42 msgid "September" msgstr "Men Fmhair" #: ../lib/SimpleCal.pm:44 msgid "October" msgstr "Deireadh Fmhair" #: ../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 "Mirt" #: ../lib/SimpleCal.pm:73 msgid "Wed" msgstr "Cad" #: ../lib/SimpleCal.pm:74 msgid "Thu" msgstr "Dar" #: ../lib/SimpleCal.pm:75 msgid "Fri" msgstr "Aoine" #: ../lib/SimpleCal.pm:76 msgid "Sat" msgstr "Sath" #~ msgid "Welcome to {package}!\n" #~ msgstr "Bidh {package} agat agus filte!\n" #~ msgid "Bye.\n" #~ msgstr "Sln leat!\n" Log-Report-Lexicon-1.12/t/simplecal/ar.po0000644000175000001440000000325514775703615020746 0ustar00markovusers00000000000000# Arabic translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Arabic \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" Log-Report-Lexicon-1.12/t/simplecal/nl.po0000644000175000001440000000327414775703615020756 0ustar00markovusers00000000000000# Dutch translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Dutch \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" Log-Report-Lexicon-1.12/t/simplecal/ru.po0000644000175000001440000000330714775703615020770 0ustar00markovusers00000000000000# Russian translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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:21+0200\n" "Last-Translator: Guido Flohr \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-5\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" Log-Report-Lexicon-1.12/t/simplecal/de_AT.po0000644000175000001440000000306714775703615021321 0ustar00markovusers00000000000000# German (Austria) translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: German \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 "Jnner" #: ../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 "" Log-Report-Lexicon-1.12/t/simplecal/it.po0000644000175000001440000000331614775703615020756 0ustar00markovusers00000000000000# Italian translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Italian \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" Log-Report-Lexicon-1.12/t/simplecal/README0000644000175000001440000000030314775703615020653 0ustar00markovusers00000000000000The files in this directory are taken from the Locale::TextDomain example. It is stripped to contain only some PO-files to test the correct processing of character-sets by Log::Report::Lexicon. Log-Report-Lexicon-1.12/t/simplecal/de.po0000644000175000001440000000342214775703615020730 0ustar00markovusers00000000000000# German translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: German \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 "Mrz" #: ../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 "Tschss!\n" Log-Report-Lexicon-1.12/t/simplecal/cs.po0000644000175000001440000000334514775703615020751 0ustar00markovusers00000000000000# Czech translations for SimpleCal. # Copyright (C) 2012 Jan Tomasek . # 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-17 11:53+0300\n" "PO-Revision-Date: 2003-07-28 04:21+0200\n" "Last-Translator: Jan Tomášek \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../lib/SimpleCal.pm:26 msgid "January" msgstr "Leden" #: ../lib/SimpleCal.pm:28 msgid "February" msgstr "Únor" #: ../lib/SimpleCal.pm:30 msgid "March" msgstr "Březen" #: ../lib/SimpleCal.pm:32 msgid "April" msgstr "Duben" #: ../lib/SimpleCal.pm:34 msgid "May" msgstr "Květen" #: ../lib/SimpleCal.pm:36 msgid "June" msgstr "Červen" #: ../lib/SimpleCal.pm:38 msgid "July" msgstr "Červenec" #: ../lib/SimpleCal.pm:40 msgid "August" msgstr "Září" #: ../lib/SimpleCal.pm:42 msgid "September" msgstr "Říjen" #: ../lib/SimpleCal.pm:44 msgid "October" msgstr "Listopad" #: ../lib/SimpleCal.pm:46 msgid "November" msgstr "Prosinec" #: ../lib/SimpleCal.pm:48 msgid "December" msgstr "Prosinec" #: ../lib/SimpleCal.pm:70 msgid "Sun" msgstr "Ne" #: ../lib/SimpleCal.pm:71 msgid "Mon" msgstr "Po" #: ../lib/SimpleCal.pm:72 msgid "Tue" msgstr "Út" #: ../lib/SimpleCal.pm:73 msgid "Wed" msgstr "St" #: ../lib/SimpleCal.pm:74 msgid "Thu" msgstr "Čt" #: ../lib/SimpleCal.pm:75 msgid "Fri" msgstr "Pá" #: ../lib/SimpleCal.pm:76 msgid "Sat" msgstr "So" #~ msgid "Welcome to {package}!\n" #~ msgstr "Vítejte v {package}!\n" #~ msgid "Bye.\n" #~ msgstr "Nashledanou!\n" Log-Report-Lexicon-1.12/t/simplecal/pt.po0000644000175000001440000000332514775703615020765 0ustar00markovusers00000000000000# Portuguese translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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:24+0200\n" "Last-Translator: Guido Flohr \n" "Language-Team: Portuguese \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 "Janeiro" #: ../lib/SimpleCal.pm:28 msgid "February" msgstr "Fevereiro" #: ../lib/SimpleCal.pm:30 msgid "March" msgstr "Maro" #: ../lib/SimpleCal.pm:32 msgid "April" msgstr "Abril" #: ../lib/SimpleCal.pm:34 msgid "May" msgstr "Maio" #: ../lib/SimpleCal.pm:36 msgid "June" msgstr "Junho" #: ../lib/SimpleCal.pm:38 msgid "July" msgstr "Julho" #: ../lib/SimpleCal.pm:40 msgid "August" msgstr "Agosto" #: ../lib/SimpleCal.pm:42 msgid "September" msgstr "Setembro" #: ../lib/SimpleCal.pm:44 msgid "October" msgstr "Outubro" #: ../lib/SimpleCal.pm:46 msgid "November" msgstr "Novembro" #: ../lib/SimpleCal.pm:48 msgid "December" msgstr "Dezembro" #: ../lib/SimpleCal.pm:70 msgid "Sun" msgstr "Dom" #: ../lib/SimpleCal.pm:71 msgid "Mon" msgstr "Seg" #: ../lib/SimpleCal.pm:72 msgid "Tue" msgstr "Ter" #: ../lib/SimpleCal.pm:73 msgid "Wed" msgstr "Qua" #: ../lib/SimpleCal.pm:74 msgid "Thu" msgstr "Qui" #: ../lib/SimpleCal.pm:75 msgid "Fri" msgstr "Sex" #: ../lib/SimpleCal.pm:76 msgid "Sat" msgstr "Sb" #~ msgid "Welcome to {package}!\n" #~ msgstr "Bem-vindo ao {package}!\n" #~ msgid "Bye.\n" #~ msgstr "At a vista!\n" Log-Report-Lexicon-1.12/t/simplecal/ar_SA.mo0000644000175000001440000000145214775703615021323 0ustar00markovusers00000000000000 017>GPX]bhlu }F    $    AprilAugustDecemberFebruaryJanuaryJulyJuneMarchMayNovemberOctoberSeptemberProject-Id-Version: SimpleCal Report-Msgid-Bugs-To: POT-Creation-Date: 2003-07-28 04:27+0200 PO-Revision-Date: 2003-07-27 18:56+0200 Last-Translator: Guido Flohr Language-Team: Arabic MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-6 Content-Transfer-Encoding: 8bit Log-Report-Lexicon-1.12/t/simplecal/ar_SA.po0000644000175000001440000000310314775703615021321 0ustar00markovusers00000000000000# Arabic translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Arabic \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 "" Log-Report-Lexicon-1.12/t/simplecal/pt_BR.po0000644000175000001440000000316114775703615021346 0ustar00markovusers00000000000000# Brazilian Portuguese translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: Brazilian Portuguese \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 "janeiro" #: ../lib/SimpleCal.pm:28 msgid "February" msgstr "fevereiro" #: ../lib/SimpleCal.pm:30 msgid "March" msgstr "maro" #: ../lib/SimpleCal.pm:32 msgid "April" msgstr "abril" #: ../lib/SimpleCal.pm:34 msgid "May" msgstr "maio" #: ../lib/SimpleCal.pm:36 msgid "June" msgstr "junho" #: ../lib/SimpleCal.pm:38 msgid "July" msgstr "julho" #: ../lib/SimpleCal.pm:40 msgid "August" msgstr "agosto" #: ../lib/SimpleCal.pm:42 msgid "September" msgstr "setembro" #: ../lib/SimpleCal.pm:44 msgid "October" msgstr "outubro" #: ../lib/SimpleCal.pm:46 msgid "November" msgstr "novembro" #: ../lib/SimpleCal.pm:48 msgid "December" msgstr "dezembro" #: ../lib/SimpleCal.pm:70 msgid "Sun" msgstr "Dom" #: ../lib/SimpleCal.pm:71 msgid "Mon" msgstr "Seg" #: ../lib/SimpleCal.pm:72 msgid "Tue" msgstr "Ter" #: ../lib/SimpleCal.pm:73 msgid "Wed" msgstr "Qua" #: ../lib/SimpleCal.pm:74 msgid "Thu" msgstr "Qui" #: ../lib/SimpleCal.pm:75 msgid "Fri" msgstr "Sex" #: ../lib/SimpleCal.pm:76 msgid "Sat" msgstr "Sb" Log-Report-Lexicon-1.12/t/simplecal/fr.po0000644000175000001440000000330614775703615020750 0ustar00markovusers00000000000000# French translations for SimpleCal. # Copyright (C) 2003 Imperia AG, Guido Flohr . # 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 \n" "Language-Team: French \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 "fvrier" #: ../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 "aot" #: ../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 "dcembre" #: ../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" Log-Report-Lexicon-1.12/t/20pot_read.t0000644000175000001440000000602614775703615020156 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Lexicon POT use warnings; use strict; use lib 'lib', '../lib'; use utf8; use Test::More tests => 47; use File::Basename qw/dirname/; use File::Spec::Functions qw/catfile/; use Encode qw/is_utf8/; use_ok('Log::Report::Lexicon::PO'); use_ok('Log::Report::Lexicon::POT'); my $sl_po = catfile(dirname(__FILE__), 'hello-world-slovak.po'); # # Try reading complex example # slightly modified from gettext examples in slovak # my $pot = Log::Report::Lexicon::POT->read($sl_po, charset => 'utf-8'); ok(defined $pot, "read pot file"); isa_ok($pot, 'Log::Report::Lexicon::POT'); # # header # is($pot->header('mime-version'), '1.0', 'access to header'); # # plurals # cmp_ok($pot->nrPlurals, '==', 4, 'test plural evaluation'); cmp_ok($pot->pluralIndex(0), '==', 0); cmp_ok($pot->pluralIndex(1), '==', 1); cmp_ok($pot->pluralIndex(2), '==', 2); cmp_ok($pot->pluralIndex(3), '==', 3); cmp_ok($pot->pluralIndex(4), '==', 3); cmp_ok($pot->pluralIndex(5), '==', 0); cmp_ok($pot->pluralIndex(6), '==', 0); cmp_ok($pot->pluralIndex(101), '==', 1); # # extended single case # my $po = $pot->msgid('Hello, world!'); ok(defined $po, "got greeting"); isa_ok($po, 'Log::Report::Lexicon::PO'); is($po->msgid, 'Hello, world!'); ok(!defined $po->plural); is($po->comment, 'translator comment translator comment line 2 '); is($po->automatic, 'automatic comment automatic comment line 2 '); my @refs = sort $po->references; cmp_ok(scalar @refs, '==', 4); is($refs[0], 'bis'); is($refs[1], 'hello-1.pl.in:18'); is($refs[2], 'hello-1.pl.in:20'); is($refs[3], 'hello-2.pl.in:13'); is($po->msgstr, "Pozdravljen, svet!"); is($po->msgstr(0), "Pozdravljen, svet!"); is($po->msgstr(1), "Pozdravljen, svet!"); # index gets ignored is($pot->msgstr("Hello, world!"), "Pozdravljen, svet!"); is($pot->msgstr("Hello, world!", 0), "Pozdravljen, svet!"); is($po->toString, <<'__DUMP'); # translator comment # translator comment line 2 #. automatic comment #. automatic comment line 2 #: bis hello-1.pl.in:18 hello-1.pl.in:20 hello-2.pl.in:13 msgid "Hello, world!" msgstr "Pozdravljen, svet!" __DUMP # # with plurals # is($pot->msgstr('Aap', 0), 'A', 'msgstr by plural'); is($pot->msgstr('Aap', 1), 'B'); is($pot->msgstr('Aap', 2), 'C'); is($pot->msgstr('Aap', 3), 'D'); is($pot->msgstr('Aap', 4), 'D'); is($pot->msgstr('Aap', 5), 'A'); is($pot->msgstr('Aap', 6), 'A'); is($pot->msgstr('Aap', 100), 'A'); is($pot->msgstr('Aap', 101), 'B'); is($pot->msgid('Aap')->plural, 'Apen'); # # with multi-lines and utf # my $po2 = $pot->msgid("This program is running as process number {pid}.multi-line\n"); ok(defined $po2, 'test multi'); my $po2t = $po2->msgstr; is($po2t, "Ta program teče kot proces številka {pid}.multi\tline\n"); ok(is_utf8($po2t), 'is utf8'); # ### READ charset detect # my $ar_fn = catfile dirname(__FILE__), simplecal => 'ar.po'; my $ar = Log::Report::Lexicon::POT->read($ar_fn); cmp_ok scalar $ar->translations, '==', 22; my $ar_po = $ar->msgid('December'); isa_ok $ar_po, 'Log::Report::Lexicon::PO'; is $ar_po->msgstr, 'ديسمبر'; Log-Report-Lexicon-1.12/t/11ppiexp.t0000644000175000001440000000300514775703615017660 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Extract PPI use warnings; use strict; use File::Temp qw/tempdir/; use Test::More; use Log::Report 'my-domain'; use_ok 'Log::Report::Translator::POT'; BEGIN { eval "require PPI"; plan skip_all => 'PPI not installed' if $@; plan tests => 11; use_ok('Log::Report::Extract::PerlPPI'); } my $lexicon = tempdir CLEANUP => 1; #warn "Lexicon at $lexicon"; my $rules = { gender => [ 'male', 'female' ] , formal => [ 'informal', 'formal' ] }; my $domain = textdomain 'my-domain', context_rules => $rules; isa_ok $domain, 'Log::Report::Domain'; ### Create tables my $ppi = Log::Report::Extract::PerlPPI->new(lexicon => $lexicon); ok defined $ppi, 'created parser'; isa_ok $ppi, 'Log::Report::Extract::PerlPPI'; $ppi->process( __FILE__ ); # yes, this file! $ppi->write; #### my $old = textdomain 'my-domain', 'DELETE'; # restart administration isa_ok $old, 'Log::Report::Domain', 'caught deleted'; my $translator = Log::Report::Translator::POT->new(lexicon => $lexicon); my $new = textdomain 'my-domain' , context_rules => $rules , translator => $translator; isa_ok $new, 'Log::Report::Domain', 'recreated'; cmp_ok $old, '!=', $new, 'new is really new'; $new->setContext('gender=male,formal=informal'); my $a1 = __x"{name 'Mark'; is $a1, 'Mark forgot his key', 'Mark'; my $a2 = __xn"Dear Sir,{ 'here'; is $a3, 'no context here'; Log-Report-Lexicon-1.12/t/30index.t0000644000175000001440000000465114775703615017473 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Lexicon index: discover files and character encodings use warnings; use strict; use lib 'lib', '../lib'; use utf8; use Test::More tests => 71; use File::Basename qw/dirname/; use File::Spec::Functions qw/catfile/; use Data::Dumper qw/Dumper/; $Data::Dumper::Indent = 1; $Data::Dumper::Quotekeys = 0; use_ok 'Log::Report::Translator::POT'; use_ok 'Log::Report::Lexicon::Index'; my $lexdir = dirname(__FILE__); my $pot = Log::Report::Translator::POT->new(lexicons => $lexdir); isa_ok $pot, 'Log::Report::Translator::POT'; my @lexicons = $pot->lexicons; cmp_ok scalar @lexicons, '==', 1, 'found 1 lexicon'; my $lexicon = shift @lexicons; isa_ok $lexicon, 'Log::Report::Lexicon::Index'; is $lexicon->directory, $lexdir; my $i = $lexicon->index; #warn Dumper $i; ### test the list() method my @list1 = $lexicon->list('simplecal'); cmp_ok scalar @list1, '==', 13, 'list simplecal'; my @list2 = $lexicon->list('simplecal', 'po'); cmp_ok scalar @list2, '==', 12, 'list simplecal po'; ### test the find() method my $fn0 = $lexicon->find('simplecal', 'nl'); ok defined $fn0, "found NL in $fn0"; defined $fn0 or warn Dumper $lexicon; my $fn1 = $lexicon->find('simplecal', 'nl_BE'); ok defined $fn1, "found nl_BE in $fn1"; ### get file opened with correct charset my @pots = ( [ ar => 'iso-8859-6' => 'نوفمبر' ] , [ ar_sa => 'iso-8859-6' => 'تشرين الثاني' ] , [ cs => 'UTF-8' => 'Prosinec' ] , [ de => 'iso-8859-1' => 'November' ] , [ de_at => 'iso-8859-1' => '' ] , [ fr => 'iso-8859-1' => 'novembre' ] , [ ga => 'iso-8859-1' => 'Mí na Samhna' ] , [ it => 'iso-8859-1' => 'novembre' ] , [ nl => 'iso-8859-1' => 'november' ] , [ pt => 'iso-8859-1' => 'Novembro' ] , [ pt_br => 'iso-8859-1' => 'novembro' ] , [ ru => 'ISO-8859-5' => 'Ноября' ] ); foreach (@pots) { my ($lang, $charset, $trans) = @$_; my $po = $pot->load('simplecal', $lang); ok defined $po, "got translation for $lang"; like $po->filename, qr/$lang\.[mp]o$/i, 'filename '.$po->filename; isa_ok $po, $lang eq 'ar_sa' ? 'Log::Report::Lexicon::MOTcompact' : 'Log::Report::Lexicon::POTcompact'; is $po->originalCharset, $charset, "charset $lang"; is $po->msgid('November'), $trans, "translated $lang"; } my $msg = {_domain => 'simplecal', _msgid => 'November'}; is $pot->translate($msg, 'ru', undef), 'Ноября'; Log-Report-Lexicon-1.12/t/12ctxt.t0000644000175000001440000000525214777242251017340 0ustar00markovusers00000000000000#!/usr/bin/env perl # This tries to use context dependent translations use warnings; use strict; use Test::More; use Log::Report '12test'; # domain selects table use File::Spec::Functions qw/catdir/; use File::Basename qw/dirname/; use POSIX qw/:locale_h/; # The test file was produced by the t/11 script, and then filled in by hand if($^O eq 'openbsd') { plan skip_all => "openbsd does not support LC_ALL"; exit 0; } my $got_locale = setlocale LC_ALL, 'en_US.UTF-8'; $got_locale && $got_locale eq 'en_US.UTF-8' or plan skip_all => "cannot set an en_US.UTF-8 locale"; plan tests => 16; use_ok 'Log::Report::Translator::POT'; my $rules = { gender => [ 'male', 'female' ] , style => [ 'informal', 'formal' ] }; my $translator = Log::Report::Translator::POT->new(lexicon => (dirname __FILE__)); my $domain = textdomain '12test', context_rules => $rules, translator => $translator; isa_ok $domain, 'Log::Report::Domain', 'recreated'; $domain->setContext('gender=male, style=informal'); # # Simpelest, no context # my $d1 = __x"no context {where}", where => 'here'; is $d1, 'out of context here'; # # Single context "gender", various parameter formats # my $a1 = __x"{name 'Mark'; is $a1, 'Mark forgot his key'; my $a2 = __x"{name 'Cleo', _context => 'gender=female'; is $a2, 'Cleo forgot her key'; my $a3 = __x"{name 'Hillary', _context => {gender =>'female'}; is $a3, 'Hillary forgot her key'; my $a4 = __x"{name 'Piet', _context => {gender => 'male'}; is $a4, 'Piet forgot his key'; # # Two contexts and count # my $b1 = __xn"Dear Sir,{ 'gender=female'; is $b2, "Hi darlin's,"; my $b3 = __xn"Dear Sir,{ 'style=formal'; is $b3, 'Dear Sirs,'; my $b4 = __xn"Dear Sir,{ 'gender=female,style=formal'; is $b4, 'Dear Ladies,'; my $b5 = __xn"Dear Sir,{ 'gender=female'; is $b6, "Hi love,"; my $b7 = __xn"Dear Sir,{ 'style=formal'; is $b7, 'Dear Sir,'; my $b8 = __xn"Dear Sir,{ 'gender=female,style=formal'; is $b8, 'Dear Lady,'; # Context values also available to insert my $c1 = __x"{name} is a {_context.gender}", name => 'Piet', _context => {gender => 'male'}; is $c1, 'Piet is a male'; Log-Report-Lexicon-1.12/t/12test/0000755000175000001440000000000015000465541017131 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/t/12test/en_US.UTF-8.po0000644000175000001440000000236414775703615021330 0ustar00markovusers00000000000000#. Header generated with Log::Report::Lexicon::POT 0.0 msgid "" msgstr "" "Project-Id-Version: my-domain 0.01\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: 2013-12-12 08:37+0100\n" "PO-Revision-Date: 2013-12-12 08:37+0100\n" "Last-Translator: Mark Overmeer\n" "Language-Team:\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" #: t/11ppiexp.t:60 msgctxt "gender=female style=formal" msgid "Dear Sir," msgid_plural "Dear Sirs," msgstr[0] "Dear Lady," msgstr[1] "Dear Ladies," #: t/11ppiexp.t:60 msgctxt "gender=male style=formal" msgid "Dear Sir," msgid_plural "Dear Sirs," msgstr[0] "" msgstr[1] "" #: t/11ppiexp.t:60 msgctxt "gender=female style=informal" msgid "Dear Sir," msgid_plural "Dear Sirs," msgstr[0] "Hi love," msgstr[1] "Hi darlin's," #: t/11ppiexp.t:60 msgctxt "gender=male style=informal" msgid "Dear Sir," msgid_plural "Dear Sirs," msgstr[0] "Dear friend," msgstr[1] "Hi friends," #: t/11ppiexp.t:63 msgid "no context {where}" msgstr "out of context {where}" #: t/11ppiexp.t:57 msgctxt "gender=female" msgid "{name} forgot his key" msgstr "{name} forgot her key" #: t/11ppiexp.t:57 msgctxt "gender=male" msgid "{name} forgot his key" msgstr "" Log-Report-Lexicon-1.12/t/00use.t0000644000175000001440000000202714775703615017150 0ustar00markovusers00000000000000#!/usr/bin/env perl use warnings; use strict; use Test::More tests => 10; # The versions of the following packages are reported to help understanding # the environment in which the tests are run. This is certainly not a # full list of all installed modules. my @show_versions = qw/PPI POSIX Test::Pod /; warn "Perl $]\n"; foreach my $package (sort @show_versions) { eval "require $package"; my $report = !$@ ? "version ". ($package->VERSION || 'unknown') : $@ =~ m/^Can't locate/ ? "not installed" : "reports error"; warn "$package $report\n"; } use_ok('Log::Report'); use_ok('Log::Report::Extract'); use_ok('Log::Report::Extract::Template'); use_ok('Log::Report::Lexicon::Index'); use_ok('Log::Report::Lexicon::PO'); use_ok('Log::Report::Lexicon::POT'); use_ok('Log::Report::Lexicon::POTcompact'); use_ok('Log::Report::Translator::Context'); use_ok('Log::Report::Translator'); use_ok('Log::Report::Translator::POT'); # Log::Report::Extract::PerlPPI requires optional PPI Log-Report-Lexicon-1.12/t/hello-world-slovak.po0000644000175000001440000000243114775703615022113 0ustar00markovusers00000000000000# -*- mode: po; coding: utf-8; -*- Slovenian message catalog for GNU gettext-example msgid "" msgstr "" "Project-Id-Version: hello-perl 0.14.5\n" "Report-Msgid-Bugs-To: bug-gnu-gettext@gnu.org\n" "POT-Creation-Date: 2007-04-18 15:27+0200\n" "PO-Revision-Date: 2005-09-29 13:38+0200\n" "Last-Translator: Primož Peterlin \n" "Language-Team: Slovenian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n" "%100==4 ? 3 : 0);\n" # translator comment # translator comment line 2 #. automatic comment #. automatic comment line 2 #: hello-1.pl.in:20 #: hello-1.pl.in:18 hello-2.pl.in:13 #: hello-1.pl.in:20 bis msgid "Hello, world!" msgstr "Pozdravljen, svet!" #: hello-1.pl.in:20 #, perl-format msgid "This program is running as process number %d." msgstr "Ta program teče kot proces številka %d." #: hello-2.pl.in:16 #, perl-brace-format msgid "This program is running as process number {pid}." "multi-line\n" msgstr "Ta program teče kot proces številka {pid}." "multi\tline\n" #: hello-2.pl.in:17 msgid "Aap" msgid_plural "Apen" msgstr[0] "A" msgstr[1] "B" msgstr[2] "C" msgstr[3] "D" Log-Report-Lexicon-1.12/t/41templ.t0000644000175000001440000000431114775703615017500 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Extract templates use warnings; use strict; use File::Temp qw/tempdir/; use Test::More; use Log::Report; # mode => 'DEBUG'; use Log::Report::Lexicon::POT; use Log::Report::Extract::Template; use constant MSGIDS => 12; # see after __END__ my @expect_pos = split /\n/, <<'_EXPECT'; first second third fourth fifth six six six %d seven eight nine tenth {a} eleven twelve {b} _EXPECT chomp $expect_pos[-1]; cmp_ok(scalar @expect_pos, '==', MSGIDS); my %expect_pos = map { ($_ => 1) } @expect_pos; $expect_pos{''} = 1; # header BEGIN { plan tests => 15 + MSGIDS*3; } my $lexicon = tempdir CLEANUP => 1; my $extr = Log::Report::Extract::Template->new ( lexicon => $lexicon , domain => 'my-domain' , pattern => 'TT2-loc' ); ok(defined $extr, 'created parser'); isa_ok($extr, 'Log::Report::Extract::Template'); my $found = $extr->process( __FILE__ ); # yes, this file! cmp_ok($found, '==', MSGIDS); $extr->write; my @potfns = $extr->index->list('my-domain'); cmp_ok(scalar @potfns, '==', 1, "one file created"); my $potfn = shift @potfns; ok(defined $potfn); ok(-s $potfn, "produced file $potfn has size"); #system "cat $potfn"; my $pot = Log::Report::Lexicon::POT->read($potfn, charset => 'utf-8'); ok(defined $pot, 'read translation table'); my @pos = $pot->translations('ACTIVE'); ok(@pos > 0); # (+1 for the header) cmp_ok(scalar @pos, '==', MSGIDS+1, 'correct number tests'); cmp_ok(scalar @pos, '==', scalar $pot->translations); # all active my %msgids; for my $po (@pos) { my $msgid = $po->msgid; ok(defined $msgid, "processing '$msgid'"); ok(!defined $msgids{$msgid}, 'check not double'); $msgids{$msgid}++; ok(delete $expect_pos{$msgid}, 'was expected'); } cmp_ok(scalar keys %expect_pos, '==', 0, "all msgids found"); warn "NOT FOUND: $_\n" for keys %expect_pos; __END__ Here, the example template starts [%loc("first")%] [%loc("second")%] [%loc('third')%] [% loc ( 'fourth' ) %] [% loc ( 'fifth' , params ) %] [%xloc('not found')%] [%loc('six six six')%] [% loc('%d seven|%d sevens', 7) %] [% INCLUDE header.tt title = loc("eight") loc ('nine' ) css =loc( 'tenth' ) %] [% '{a} eleven' | loc(a => 3) %] [%| loc(b=>4) %]twelve {b}[%END%] Log-Report-Lexicon-1.12/t/40ppi.t0000644000175000001440000000520514775703615017151 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Extract PPI use warnings; use strict; use File::Temp qw/tempdir/; use Test::More; use constant MSGIDS => 25; use constant PLURAL_MSGIDS => 4; BEGIN { eval "require PPI"; plan skip_all => 'PPI not installed' if $@; plan tests => 10 + MSGIDS*4 + PLURAL_MSGIDS*1; use_ok('Log::Report::Extract::PerlPPI'); } my $lexicon = tempdir CLEANUP => 1; my %expect_pos = ('' => 1); # expect header sub take($@) { my $result = shift; ok("$result", "$result"); $expect_pos{$_}++ for @_; } ### my $ppi = Log::Report::Extract::PerlPPI->new(lexicon => $lexicon); ok(defined $ppi, 'created parser'); isa_ok($ppi, 'Log::Report::Extract::PerlPPI'); $ppi->process( __FILE__ ); # yes, this file! $ppi->write; my @potfns = $ppi->index->list('first-domain'); cmp_ok(scalar @potfns, '==', 1, "one file created"); my $potfn = shift @potfns; ok(defined $potfn); ok(-s $potfn, "produced file $potfn has size"); #### sub dummy($) {shift} use Log::Report 'first-domain'; # cannot use variable textdomain take("a0"); take(__"a1", 'a1'); take((__"a2"), 'a2'); take((__"a3a", "a3b"), 'a3a'); take(__("a4"), 'a4'); take(__ dummy('a7')); take(__ dummy 'a8'); take(__(dummy 'a9')); take((__x"b2"), 'b2'); take((__x"b3a", b2b => "b3c"), 'b3a'); take(__x("b4"), 'b4'); take(__x("b5a", b5b => "b5c"), 'b5a'); take(__x('b6a', b6b => "b6c"), 'b6a'); take(__x(qq{b7a}, b7b => "b7c"), 'b7a'); take(__x(q{b8a}, b8b => "b8c"), 'b8a'); take(__x(b9a => b9b => "b9c"), 'b9a'); take(__x(b10 => 1, 2), 'b10'); take((__n "c1", "c2", 1), "c1", "c2"); take((__n "c3", "c4", 0), "c3", "c4"); take(__n("c5", "c6", 1), "c5", "c6"); take(__n("c7", "c8", 0), "c7", "c8"); take(N__("d1"), "d1", "d1"); take(join(',', N__w("d2 d3")), "d2", "d3"); take(join(',', N__w(" d4 d5 d6 d7")), "d4", "d5", "d6", "d7"); # line contains tab ### do not index these: __x(+"e1"); ### check that all tags were found in POT my $pot = Log::Report::Lexicon::POT->read($potfn, charset => 'utf-8'); ok(defined $pot, 'read translation table'); my @pos = $pot->translations('ACTIVE'); ok(@pos > 0); cmp_ok(scalar @pos, '==', MSGIDS, 'correct number tests'); cmp_ok(scalar @pos, '==', scalar $pot->translations); # all active my %msgids; for my $po (@pos) { my $msgid = $po->msgid; ok(defined $msgid, "processing $msgid"); ok(!defined $msgids{$msgid}, 'check not double'); $msgids{$msgid}++; ok(delete $expect_pos{$msgid}, "was expected $msgid"); my $plural = $po->plural or next; ok(delete $expect_pos{$plural}, 'plural was expected'); } cmp_ok(scalar keys %expect_pos, '==', 0, "all msgids found"); warn "NOT FOUND: $_\n" for keys %expect_pos; Log-Report-Lexicon-1.12/t/21pot_modif.t0000644000175000001440000001027714775703615020345 0ustar00markovusers00000000000000#!/usr/bin/env perl # Try Lexicon PO modifications use warnings; use strict; use utf8; use Test::More tests => 29; use_ok('Log::Report::Lexicon::PO'); use_ok('Log::Report::Lexicon::POT'); # # Create header # $Log::Report::VERSION = 'SOME_VERSION'; my $pot = Log::Report::Lexicon::POT->new ( textdomain => 'log-report' , version => '2.3' , charset => 'UTF-8' , date => 'DUMMY' # don't want this to change during test ); is($pot->msgstr(''), <<'__HEADER'); Project-Id-Version: log-report 2.3 Report-Msgid-Bugs-To: POT-Creation-Date: DUMMY PO-Revision-Date: DUMMY Last-Translator: Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n!=1); __HEADER is($pot->msgid('')->toString, <<'__HEAD'); #. Header generated with Log::Report::Lexicon::POT SOME_VERSION msgid "" msgstr "" "Project-Id-Version: log-report 2.3\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: DUMMY\n" "PO-Revision-Date: DUMMY\n" "Last-Translator:\n" "Language-Team:\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" __HEAD cmp_ok($pot->nrPlurals, "==", 2); is($pot->header('mime-version'), '1.0'); is($pot->header('mime-version', '3.14'), '3.14'); is($pot->header('mime-version'), '3.14'); is($pot->header('mime-version', undef), undef); is($pot->header('new-field', 'some value'), 'some value'); $pot->updated('NEWDATE'); is($pot->msgid('')->toString, <<'__HEAD'); #. Header generated with Log::Report::Lexicon::POT SOME_VERSION msgid "" msgstr "" "Project-Id-Version: log-report 2.3\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: DUMMY\n" "PO-Revision-Date: NEWDATE\n" "Last-Translator:\n" "Language-Team:\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" "new-field: some value\n" __HEAD # # Create non-plural # my $po = Log::Report::Lexicon::PO->new ( msgid => 'aap' , references => 'aap.pm:10' ); is($po->toString, <<'__AAP', 'no translation'); #: aap.pm:10 msgid "aap" msgstr "" __AAP $po->addReferences('monkey.pm:12 aap.pm:3'); $po->msgstr(0, 'monkey'); is($po->toString, <<'__AAP', 'with translation'); #: aap.pm:10 aap.pm:3 monkey.pm:12 msgid "aap" msgstr "monkey" __AAP is($po->plural("apen"), 'apen', 'add plural'); ok($po->fuzzy(1), 'is fuzzy'); is($po->toString, <<'__AAP'); #: aap.pm:10 aap.pm:3 monkey.pm:12 #, fuzzy msgid "aap" msgid_plural "apen" msgstr[0] "monkey" msgstr[1] "" __AAP is($po->toString(nr_plurals => $pot->nrPlurals), <<'__AAP'); #: aap.pm:10 aap.pm:3 monkey.pm:12 #, fuzzy msgid "aap" msgid_plural "apen" msgstr[0] "monkey" msgstr[1] "" __AAP $po->msgstr(1, 'monkeys'); $po->fuzzy(0); cmp_ok($po->removeReferencesTo('aap.pm'), '==', 1); is($po->toString(nr_plurals => $pot->nrPlurals), <<'__AAP'); #: monkey.pm:12 msgid "aap" msgid_plural "apen" msgstr[0] "monkey" msgstr[1] "monkeys" __AAP # # Index # ok(!$pot->msgid('aap')); is($pot->add($po), $po, 'add'); is($pot->msgid('aap'), $po); is($pot->msgstr('aap', 0), 'monkeys'); is($pot->msgstr('aap', 1), 'monkey'); is($pot->msgstr('aap', 2), 'monkeys'); # # disable/enable # cmp_ok($po->removeReferencesTo('monkey.pm'), "==", 0, 'rm last ref'); is($po->toString(nr_plurals => $pot->nrPlurals), <<'__AAP'); #~ msgid "aap" #~ msgid_plural "apen" #~ msgstr[0] "monkey" #~ msgstr[1] "monkeys" __AAP $po->addReferences('noot.pm:12', 'aap.pm:42'); is($po->toString(nr_plurals => $pot->nrPlurals), <<'__AAP'); #: aap.pm:42 noot.pm:12 msgid "aap" msgid_plural "apen" msgstr[0] "monkey" msgstr[1] "monkeys" __AAP # # Write # my $text = ''; open TEXT, '>:utf8', \$text; $pot->write(\*TEXT); close TEXT; is($text, <<'__ALL') #. Header generated with Log::Report::Lexicon::POT SOME_VERSION msgid "" msgstr "" "Project-Id-Version: log-report 2.3\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: DUMMY\n" "PO-Revision-Date: NEWDATE\n" "Last-Translator:\n" "Language-Team:\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" "new-field: some value\n" #: aap.pm:42 noot.pm:12 msgid "aap" msgid_plural "apen" msgstr[0] "monkey" msgstr[1] "monkeys" __ALL Log-Report-Lexicon-1.12/t/10context.t0000644000175000001440000000522314775703615020042 0ustar00markovusers00000000000000#!/usr/bin/env perl # Check message context parsing and checking use warnings; use strict; use lib 'lib', '../lib'; use Test::More tests => 14; use Log::Report; use Log::Report::Translator; use Log::Report::Translator::Context; my $rules = +{ style => [ 'informal', 'formal' ] , gender => [ 'male', 'female', 'unknown' ] , gender2 => { alternatives => [ 'male', 'female' ] } , gender3 => { default => { male => 'm1' , female => 'f1' , unknown => 'm1' } , nl => { unknown => 'x' } } }; #use JSON; #warn JSON->new->pretty->encode(\%config); my $context = Log::Report::Translator::Context->new(rules => $rules); is_deeply [$context->expand('a{b 'en')] , [ 'a{b}c', [ 'gender=female', 'gender=male', 'gender=unknown' ]] , 'simple gender'; is_deeply [$context->ctxtFor(__('a{b 'en', {gender => 'male'})] , [ 'a{b}c', 'gender=male' ]; is_deeply [$context->expand('a{ 'en')] , [ 'ab', [ 'style=formal', 'style=informal']] , 'simple boolean'; is_deeply [$context->ctxtFor(__('a{ 'en', {style => 'formal'})] , [ 'ac', 'style=formal' ]; is_deeply [$context->ctxtFor(__('a{ 'en', {style => 'informal'})] , [ 'ac', 'style=informal' ]; is_deeply [ $context->expand("a{b 'en') ] , [ 'a{b%2f}c' , [ 'gender=female style=formal' , 'gender=female style=informal' , 'gender=male style=formal' , 'gender=male style=informal' , 'gender=unknown style=formal' , 'gender=unknown style=informal' ] ] , 'combination explosion'; is_deeply [$context->ctxtFor(__('a{b 'en' , {gender => 'female', style => 'informal'})] , [ 'a{b%2f}c', 'gender=female style=informal' ]; is_deeply [$context->expand('{ 'en')] , [ 'a', [ 'gender2=female', 'gender2=male' ] ] , 'deeper gender2 alternatives'; is_deeply [$context->ctxtFor(__('{ 'en', {gender2 => 'female'})] , [ 'a', "gender2=female" ]; is_deeply [$context->expand('a{ctxtFor(__('a{ 'en', {gender3 => 'male'})] , [ 'a', "gender3=m1" ]; is_deeply [$context->ctxtFor(__('a{ 'en', {gender3 => 'unknown'})] , [ 'ax', "gender3=m1" ]; is_deeply [$context->expand('c{dctxtFor(__('c{d 'nl', {gender3 => 'unknown'})] , [ 'c{d}e', "gender3=x" ]; Log-Report-Lexicon-1.12/t/04locale.t0000644000175000001440000000321514775703615017617 0ustar00markovusers00000000000000#!/usr/bin/env perl # test locale use Test::More; use POSIX; my $alt_locale; BEGIN { eval "POSIX->import( qw/setlocale :locale_h/ )"; # locale disabled? defined setlocale(LC_ALL, 'C') or plan skip_all => "no translation support in Perl or OS"; LOCALE: foreach my $l (qw/nl_NL de_DE pt_PT tr_TR/) # only non-english! { foreach my $c ('utf-8', 'iso-8859-1', '') { $alt_locale = $c ? "$l.$c" : $l; my $old = setlocale LC_ALL, $alt_locale; my $set = setlocale LC_ALL, $alt_locale; last LOCALE if defined $set && $set eq $alt_locale; } undef $alt_locale; } defined $alt_locale or plan skip_all => "cannot find alternative language for tests"; plan tests => 10; } ok(1, "alt locale: $alt_locale"); ok(defined setlocale(LC_ALL, 'C'), 'set C'); my $try = setlocale(LC_ALL); ok(defined $try, 'explicit C found'); ok($try eq 'C' || $try eq 'POSIX'); $! = 2; my $err_posix = "$!"; ok(defined $err_posix, $err_posix); # english my $change = setlocale LC_ALL, $alt_locale; ok(defined $change, "returned change to alternative locale"); is(setlocale(LC_ALL), $alt_locale, "set to $alt_locale successful?"); $! = 2; my $err_alt = "$!"; ok(defined $err_alt, $err_alt); if($err_posix eq $err_alt) { # some platforms have mistakes in their language configuration ok(1, "ERROR: libc translations not switched"); warn "*** ERROR: changing language of libc error messages did not work\n"; sleep 1; } else { ok(1, "libc does translate standard errors"); } setlocale(LC_ALL, 'C'); $! = 2; my $err_posix2 = "$!"; is($err_posix, $err_posix2, $err_posix2); Log-Report-Lexicon-1.12/MANIFEST0000644000175000001440000000320015000465541016670 0ustar00markovusers00000000000000ChangeLog MANIFEST Makefile.PL README bin/xgettext-perl lib/Log/Report/Extract.pm lib/Log/Report/Extract.pod lib/Log/Report/Extract/PerlPPI.pm lib/Log/Report/Extract/PerlPPI.pod lib/Log/Report/Extract/Template.pm lib/Log/Report/Extract/Template.pod lib/Log/Report/Lexicon.pm lib/Log/Report/Lexicon.pod lib/Log/Report/Lexicon/Index.pm lib/Log/Report/Lexicon/Index.pod lib/Log/Report/Lexicon/MOTcompact.pm lib/Log/Report/Lexicon/MOTcompact.pod lib/Log/Report/Lexicon/PO.pm lib/Log/Report/Lexicon/PO.pod lib/Log/Report/Lexicon/POT.pm lib/Log/Report/Lexicon/POT.pod lib/Log/Report/Lexicon/POTcompact.pm lib/Log/Report/Lexicon/POTcompact.pod lib/Log/Report/Lexicon/Table.pm lib/Log/Report/Lexicon/Table.pod lib/Log/Report/Translator/Context.pm lib/Log/Report/Translator/Context.pod lib/Log/Report/Translator/Gettext.pm lib/Log/Report/Translator/Gettext.pod lib/Log/Report/Translator/POT.pm lib/Log/Report/Translator/POT.pod lib/Log/Report/Win32Locale.pm lib/Log/Report/Win32Locale.pod lib/Log/Report/messages/log-report-lexicon/nl_NL.po t/00use.t t/04locale.t t/10context.t t/11ppiexp.t t/12ctxt.t t/12test/en_US.UTF-8.po t/20pot_read.t t/21pot_modif.t t/22compact.t t/30index.t t/40ppi.t t/41templ.t t/hello-world-slovak.po t/simplecal/README t/simplecal/ar.po t/simplecal/ar_SA.mo t/simplecal/ar_SA.po t/simplecal/cs.po t/simplecal/de.po t/simplecal/de_AT.po t/simplecal/fr.po t/simplecal/ga.po t/simplecal/it.po t/simplecal/nl.po t/simplecal/pt.po t/simplecal/pt_BR.po t/simplecal/ru.po xt/99pod.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Log-Report-Lexicon-1.12/lib/0000755000175000001440000000000015000465541016312 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/0000755000175000001440000000000015000465541017033 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/0000755000175000001440000000000015000465541020306 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/messages/0000755000175000001440000000000015000465541022115 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/messages/log-report-lexicon/0000755000175000001440000000000015000465541025646 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/messages/log-report-lexicon/nl_NL.po0000644000175000001440000002366414777242251027236 0ustar00markovusers00000000000000#. Header generated with Log::Report::Lexicon::POT 0.0 msgid "" msgstr "" "Project-Id-Version: log-report 0.01\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: 2007-05-14 17:14+0200\n" "PO-Revision-Date: 2025-04-14 17:31+0200\n" "Last-Translator: Mark Overmeer \n" "Language-Team:\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" #: lib/Log/Report/Translator/POT.pm:99 #, fuzzy msgid "You have to upgrade Log::Report::Lexicon to at least 1.00" msgstr "" #: lib/Log/Report/Extract.pm:54 msgid "cannot create lexicon directory {dir}" msgstr "kan lexicon map {dir} niet aanmaken" #: lib/Log/Report/Lexicon/MOTcompact.pm:168 lib/Log/Report/Lexicon/POT.pm:180 #: lib/Log/Report/Lexicon/POTcompact.pm:90 #, fuzzy msgid "cannot detect charset in {fn}" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:163 lib/Log/Report/Lexicon/POTcompact.pm:77 #, fuzzy msgid "cannot read from file {fn} (unknown charset)" msgstr "" #: lib/Log/Report/Lexicon/POTcompact.pm:72 #, fuzzy msgid "cannot read in {charset} from file {fn}" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:158 msgid "cannot read in {cs} from file {fn}" msgstr "kan bestand {fn} niet lezen in {cs}" #: lib/Log/Report/Lexicon/MOTcompact.pm:89 #, fuzzy msgid "cannot read magic from {fn}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:81 #, fuzzy msgid "cannot read mo from file {fn}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:142 #, fuzzy msgid "cannot read msgids from {fn}, need {size} at {loc}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:113 #, fuzzy msgid "cannot read originals from {fn}, need {size} at {loc}" msgstr "" #: lib/Log/Report/Extract/PerlPPI.pm:134 #, fuzzy msgid "cannot read perl from file {filename}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:99 #, fuzzy msgid "cannot read superblock from {fn}" msgstr "" #: lib/Log/Report/Extract/Template.pm:114 msgid "cannot read template from {fn}" msgstr "template {fn} kan niet worden gelezen" #: lib/Log/Report/Lexicon/MOTcompact.pm:124 #: lib/Log/Report/Lexicon/MOTcompact.pm:152 #, fuzzy msgid "cannot read translations from {fn}, need {size} at {loc}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:136 #, fuzzy msgid "cannot seek to {loc} in {fn} for msgid strings" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:109 #, fuzzy msgid "cannot seek to {loc} in {fn} for originals" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:147 #, fuzzy msgid "cannot seek to {loc} in {fn} for transl strings" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:120 #, fuzzy msgid "cannot seek to {loc} in {fn} for translations" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:237 #, fuzzy msgid "cannot write to file {fn} with {layers}" msgstr "kan bestand {fn} niet schrijven in {layers}" #: lib/Log/Report/Extract/PerlPPI.pm:277 #, fuzzy msgid "count missing in {function} in line {line}" msgstr "" #: lib/Log/Report/Extract/PerlPPI.pm:248 msgid "do not interpolate in msgid (found '{var}' in line {line})" msgstr "gebruik geen variabelen in een msgid (vond '{var}' op regel {line'})" #: lib/Log/Report/Lexicon/PO.pm:398 msgid "do not understand command '{cmd}' at {where}" msgstr "commando '{cmd}' op plaats {where} niet begrepen" #: lib/Log/Report/Lexicon/PO.pm:415 msgid "" "do not understand line at {where}:\n" " {line}" msgstr "" "de regel op {where} wordt niet begrepen:\n" " {line}" #: lib/Log/Report/Extract.pm:51 msgid "extractions require an explicit lexicon directory" msgstr "een expliciete lexicon directory is nodig voor de uittreksels" #: lib/Log/Report/Lexicon/MOTcompact.pm:183 lib/Log/Report/Lexicon/POT.pm:194 #: lib/Log/Report/Lexicon/POTcompact.pm:136 msgid "failed reading from file {fn}" msgstr "lezen uit bestand {fn} mislukt" #: lib/Log/Report/Extract.pm:236 msgid "found one pot file for domain {domain}" msgid_plural "found {_count} pot files for domain {domain}" msgstr[0] "één pot bestand voor domein {domain} gevonden" msgstr[1] "{_count} pot bestanden voor domain {domain} gevonden" #: lib/Log/Report/Lexicon/POTcompact.pm:88 #, fuzzy msgid "header not found for charset in {fn}" msgstr "" #: lib/Log/Report/Lexicon/Table.pm:125 msgid "invalid plural-form algorithm '{alg}'" msgstr "incorrect meervoudsvorm algoritme '{alg}'" #: lib/Log/Report/Extract/Template.pm:141 #, fuzzy msgid "msgid '{msgid}' contains html escapes, don't do that. File {fn} line {linenr}" msgstr "" #: lib/Log/Report/Extract/Template.pm:109 msgid "need pattern to scan for, either via new() or process()" msgstr "een scan pattern is nodig, via new() of process()" #: lib/Log/Report/Extract/PerlPPI.pm:266 msgid "new-line is added automatically (found in line {line})" msgstr "een regel-overgang wordt automatisch toegevoegd (gevonden op regel {line})" #: lib/Log/Report/Extract/PerlPPI.pm:138 #, fuzzy msgid "no Perl in file {filename}" msgstr "" #: lib/Log/Report/Translator/Context.pm:117 #, fuzzy msgid "no context definition for `{tag}' in `{msgid}'" msgstr "" #: lib/Log/Report/Extract.pm:276 #, fuzzy msgid "no context tags allowed in plural `{msgid}'" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:227 msgid "no filename or file-handle specified for PO" msgstr "geen bestandsnaam of -handle meegegeven voor PO" #: lib/Log/Report/Lexicon/POT.pm:382 msgid "no header defined in POT for file {fn}" msgstr "geen kop opgegeven in POT in bestand {fn}" #: lib/Log/Report/Lexicon/PO.pm:421 msgid "no msgid in block {where}" msgstr "geen msgid in blok {where}" #: lib/Log/Report/Lexicon/PO.pm:500 msgid "no plurals for '{msgid}'" msgstr "geen meervoudsvormen voor '{msgid}'" #: lib/Log/Report/Extract/PerlPPI.pm:202 msgid "no text-domain for translatable at {fn} line {line}" msgstr "geen text-domain voor vertaalbare string in {fn} regel {line}" #: lib/Log/Report/Translator/Context.pm:125 #, fuzzy msgid "no value for tag `{tag}' in the context" msgstr "" #: lib/Log/Report/Extract/PerlPPI.pm:142 lib/Log/Report/Extract/Template.pm:106 msgid "processing file {fn} in {charset}" msgstr "verwerk bestand {fn} in {charset}" #: lib/Log/Report/Lexicon/PO.pm:410 msgid "quoted line is not a continuation at {where}" msgstr "regel met quotes is geen voortzetting in {where}" #: lib/Log/Report/Translator/POT.pm:176 #, fuzzy msgid "read table {filename} as {class} for {dname} in {locale}" msgstr "" #: lib/Log/Report/Extract.pm:245 msgid "starting new textdomain {domain}, template in {filename}" msgstr "begin van nieuw textdomain {domain}, sjabloon in {filename}" #: lib/Log/Report/Lexicon/POTcompact.pm:199 msgid "string '{text}' not between quotes at {location}" msgstr "tekst '{text}' niet tussen quotes in {location}" #: lib/Log/Report/Extract/PerlPPI.pm:256 msgid "string is incorrect at line {line}: {error}" msgstr "foutieve string in regel {regel}: {error}" #: lib/Log/Report/Translator/Context.pm:156 #, fuzzy msgid "tags value must have form `a=b', found `{this}' in `{source}'" msgstr "" #: lib/Log/Report/Extract/Template.pm:170 #, fuzzy msgid "template syntax error, no END in {fn} line {line}" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:119 msgid "textdomain parameter is required" msgstr "tekstdomain argument is verplicht" #: lib/Log/Report/Lexicon/MOTcompact.pm:165 #, fuzzy msgid "the header is not the first entry, needed for charset in {fn}" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:361 msgid "the only acceptable parameter is 'ACTIVE', not '{p}'" msgstr "het enige geaccepteerde argument is 'ACTIVE', niet '{p}'" #: lib/Log/Report/Lexicon/Table.pm:103 #, fuzzy msgid "there is no Plural-Forms field in the header, but needed" msgstr "" #: lib/Log/Report/Lexicon/PO.pm:489 msgid "too many plurals for '{msgid}'" msgstr "te veel meervouden voor '{msgid}'" #: lib/Log/Report/Lexicon/POT.pm:341 #, fuzzy msgid "translation already exists for '{msgid}' with '{ctxt}" msgstr "" #: lib/Log/Report/Translator/Context.pm:129 #, fuzzy msgid "unknown alternative `{alt}' for tag `{tag}' in context of `{msgid}'" msgstr "" #: lib/Log/Report/Lexicon/PO.pm:372 msgid "unknown comment type '{cmd}' at {where}" msgstr "onbekend commentaar type '{cmd}' in {where}" #: lib/Log/Report/Translator/Context.pm:180 #, fuzzy msgid "unknown context tag '{tag}' used in '{msgid}'" msgstr "" #: lib/Log/Report/Lexicon/PO.pm:340 msgid "unknown flag {flag} ignored" msgstr "onbekende vlag {flag} wordt genegeerd" #: lib/Log/Report/Extract/Template.pm:130 #, fuzzy msgid "unknown pattern {pattern}" msgstr "" #: lib/Log/Report/Translator/POT.pm:174 #, fuzzy msgid "unknown translation table extension '{ext}' in {filename}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:172 lib/Log/Report/Lexicon/POT.pm:185 #: lib/Log/Report/Lexicon/POTcompact.pm:92 #, fuzzy msgid "unsupported charset {charset} in {fn}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:67 #, fuzzy msgid "unsupported explicit charset {charset} for {fn}" msgstr "" #: lib/Log/Report/Lexicon/MOTcompact.pm:94 #, fuzzy msgid "unsupported file type (magic number is {magic%x})" msgstr "" #: lib/Log/Report/Extract/PerlPPI.pm:186 #, fuzzy msgid "use double quotes not single, in {string} on {file} line {line}" msgstr "" #: lib/Log/Report/Lexicon/POT.pm:258 msgid "write errors for file {fn}" msgstr "schrijfproblemen bij bestand {fn}" #: lib/Log/Report/Extract.pm:183 msgid "{domain}: one file with {ids} msgids" msgid_plural "{domain}: {_count} files with each {ids} msgids" msgstr[0] "{domain}: één bestand met {ids} mgsids" msgstr[1] "{domain}: {_count} bestanden met elk {ids} msgids" #: lib/Log/Report/Extract.pm:176 msgid "{domain}: one file with {ids} msgids, {f} fuzzy and {i} inactive translations" msgid_plural "{domain}: {_count} files each {ids} msgids, {f} fuzzy and {i} inactive translations in total" msgstr[0] "{domain}: één bestand met {ids} mgsids, {f} fuzzy en {i} op non-actief" msgstr[1] "{domain}: {_count} bestanden met elk {ids} msgids, {f} fuzzy en {i} op non-actief in het totaal" #: lib/Log/Report/Extract.pm:166 msgid "{domain}: {fuzzy%3d} fuzzy, {inact%3d} inactive in {filename}" msgstr "{domain}: {fuzzy%3d} fuzzy, {inact%3d} op non-actief in {filename}" Log-Report-Lexicon-1.12/lib/Log/Report/Win32Locale.pm0000644000175000001440000003731115000465534022675 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Win32Locale;{ our $VERSION = '1.12'; } use base 'Exporter'; use warnings; use strict; use Log::Report 'log-report-lexicon'; our @EXPORT = qw/codepage_to_iso iso_to_codepage iso_locale charset_encoding ms_codepage_id ms_install_codepage_id ms_locale/; use Win32::TieRegistry; my %codepage2iso; my %localewin2iso; my %charsetwin; while() { my ($codepage, $iso, $localewin, $charsetwin, $name) = split /\,\s*/, $_, 5; defined $name or die "Missing field in '$_'"; $codepage2iso{hex $codepage} = $iso; $localewin2iso{$localewin} = $iso; $charsetwin{$localewin} = $charsetwin; } my %iso2codepage = reverse %codepage2iso; close DATA; sub codepage_to_iso($) { my $cp = shift; defined $cp ? $codepage2iso{$cp =~ m/^0x/i ? hex($cp) : $cp} : (); } sub iso_to_codepage($) { my $iso = shift; return $iso2codepage{$iso} if $iso2codepage{$iso}; my ($lang) = split $iso, /\_/; $iso2codepage{$lang}; } sub iso_locale(;$) { my $locale = shift; if(defined $locale) { my $iso = $localewin2iso{$locale} || $codepage2iso{$locale}; return $iso if $iso; } codepage_to_iso(ms_codepage_id()) || codepage_to_iso(ms_locale()); } # the following functions are rewrites of Win32::Codepage version 1.00 # Copyright 2005 Clotho Advanced Media, Inc. Under perl license. # Win32 does not nicely export the functions. my $nls = 'HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Nls'; my $del = {Access => Win32::TieRegistry::KEY_READ(), Delimiter => '/'}; my $codepages = Win32::TieRegistry->new("$nls/CodePage", $del); my $languages = Win32::TieRegistry->new("$nls/Language", $del); sub charset_encoding { my $charset = $codepages->GetValue("ACP") || $codepages->GetValue("OEMCP"); $charset && $charset =~ m/^[0-9a-fA-F]+$/ ? "cp".lc($charset) : undef; } sub ms_codepage_id { my $id = $languages->GetValue("Default"); $id && $id =~ m/^[0-9a-fA-F]+$/ ? hex($id) : undef; } sub ms_install_codepage_id { my $id = $languages->GetValue("InstallLanguage"); $id && $id =~ m/^[0-9a-fA-F]+$/ ? hex($id) : undef; } # the following functions are rewrites of Win32::Locale version 0.04 # Copyright (c) 2001,2003 Sean M. Burke, Under perl license. # The module seems unmaintained, and treating the 'region' in the ISO # code as lower-case is a mistake. my $i18n = Win32::TieRegistry->new ("HKEY_CURRENT_USER/Control Panel/International", $del); sub ms_locale { my $locale = $i18n->GetValue("Locale"); $locale =~ m/^[0-9a-fA-F]+$/ ? hex($locale) : undef; } 1; # taken from http://www.microsoft.com/globaldev/nlsweb on 2007/10/22 # merged with http://docs.moodle.org/en/Table_of_locales # columns: codepage,ISO,localewin,localewincharset,language name # use a wide terminal and tabstop=8 # After changes, sort with :.,$!sort -t, -k2,2 __DATA__ 0x0036, af, , , Afrikaans 0x0436, af_ZA, Afrikaans_South Africa.1252, WINDOWS-1252, Afrikaans (South Africa) 0x045E, am_ET, , , Amharic (Ethiopia) 0x0001, ar, , , Arabic 0x3801, ar_AE, , , Arabic (U.A.E.) 0x3C01, ar_BH, , , Arabic (Bahrain) 0x1401, ar_DZ, , , Arabic (Algeria) 0x0C01, ar_EG, , , Arabic (Egypt) 0x0801, ar_IQ, , , Arabic (Iraq) 0x2C01, ar_JO, , , Arabic (Jordan) 0x3401, ar_KW, , , Arabic (Kuwait) 0x3001, ar_LB, , , Arabic (Lebanon) 0x1001, ar_LY, , , Arabic (Libya) 0x1801, ar_MA, , , Arabic (Morocco) 0x047A, arn_CL, , , Mapudungun (Chile) 0x2001, ar_OM, , , Arabic (Oman) 0x4001, ar_QA, , , Arabic (Qatar) 0x0401, ar_SA, Arabic_Saudi Arabia.1256, WINDOWS-1256, Arabic (Saudi Arabia) 0x2801, ar_SY, , , Arabic (Syria) 0x1C01, ar_TN, , , Arabic (Tunisia) 0x2401, ar_YE, , , Arabic (Yemen) 0x044D, as_IN, , , Assamese (India) 0x002C, az, , , Azeri 0x082C, az_Cyrl_AZ, , , Azeri (Cyrillic, Azerbaijan) 0x042C, az_Latn_AZ, , , Azeri (Latin, Azerbaijan) 0x046D, ba_RU, , , Bashkir (Russia) 0x0023, be, , , Belarusian 0x0423, be_BY, Belarusian_Belarus.1251, WINDOWS-1251, Belarusian (Belarus) 0x0002, bg, Bulgarian_Bulgaria.1251, WINDOWS-1251, Bulgarian 0x0402, bg_BG, , , Bulgarian (Bulgaria) 0x0845, bn_BD, , , Bengali (Bangladesh) 0x0445, bn_IN, Bengali (India), , Bengali (India) 0x0451, bo_CN, , , Tibetan (PRC) 0x047E, br_FR, , , Breton (France) 0x201A, bs_Cyrl_BA, , , Bosnian (Cyrillic, Bosnia and Herzegovina) 0x141A, bs_Latn_BA, Serbian (Latin), WINDOWS-1250, Bosnian (Latin, Bosnia and Herzegovina) 0x0003, ca, , , Catalan 0x0403, ca_ES, Catalan_Spain.1252, WINDOWS-1252, Catalan (Catalan) 0x0483, co_FR, , , Corsican (France) 0x0005, cs, , , Czech 0x0405, cs_CZ, Czech_Czech Republic.1250, WINDOWS-1250, Czech (Czech Republic) 0x0452, cy_GB, , , Welsh (United Kingdom) 0x0006, da, , , Danish 0x0406, da_DK, Danish_Denmark.1252, WINDOWS-1252, Danish (Denmark) 0x0007, de, , , German 0x0C07, de_AT, , , German (Austria) 0x0807, de_CH, , , German (Switzerland) 0x0407, de_DE, German_Germany.1252, WINDOWS-1252, German (Germany) 0x1407, de_LI, , , German (Liechtenstein) 0x1007, de_LU, , , German (Luxembourg) 0x0065, div, , , Divehi 0x0465, div_MV, , , Divehi (Maldives) 0x0008, el, , , Greek 0x0408, el_GR, Greek_Greece.1253, WINDOWS-1253, Greek (Greece) 0x0009, en, , , English 0x2409, en_029, , , English (Caribbean) 0x0C09, en_AU, English_Australia.1252, , English (Australia) 0x2809, en_BZ, , , English (Belize) 0x1009, en_CA, , , English (Canada) 0x0809, en_GB, , , English (United Kingdom) 0x1809, en_IE, , , English (Ireland) 0x4009, en_IN, , , English (India) 0x2009, en_JM, , , English (Jamaica) 0x4409, en_MY, , , English (Malaysia) 0x1409, en_NZ, , , English (New Zealand) 0x3409, en_PH, , , English (Republic of the Philippines) 0x4809, en_SG, , , English (Singapore) 0x2C09, en_TT, , , English (Trinidad and Tobago) 0x0409, en_US, , , English (United States) 0x1C09, en_ZA, , , English (South Africa) 0x3009, en_ZW, , , English (Zimbabwe) 0x000A, es, , , Spanish 0x2C0A, es_AR, , , Spanish (Argentina) 0x400A, es_BO, , , Spanish (Bolivia) 0x340A, es_CL, , , Spanish (Chile) 0x240A, es_CO, , , Spanish (Colombia) 0x140A, es_CR, , , Spanish (Costa Rica) 0x1C0A, es_DO, , , Spanish (Dominican Republic) 0x300A, es_EC, , , Spanish (Ecuador) 0x0C0A, es_ES, Spanish_Spain.1252, WINDOWS-1252, Spanish (Spain) 0x100A, es_GT, , , Spanish (Guatemala) 0x480A, es_HN, , , Spanish (Honduras) 0x080A, es_MX, , , Spanish (Mexico) 0x4C0A, es_NI, , , Spanish (Nicaragua) 0x180A, es_PA, , , Spanish (Panama) 0x280A, es_PE, , , Spanish (Peru) 0x500A, es_PR, , , Spanish (Puerto Rico) 0x3C0A, es_PY, , , Spanish (Paraguay) 0x440A, es_SV, , , Spanish (El Salvador) 0x540A, es_US, , , Spanish (United States) 0x380A, es_UY, , , Spanish (Uruguay) 0x200A, es_VE, , , Spanish (Venezuela) 0x0025, et, , , Estonian 0x0425, et_EE, Estonian_Estonia.1257, WINDOWS-1257, Estonian (Estonia) 0x002D, eu, , , Basque 0x042D, eu_ES, Basque_Spain.1252, WINDOWS-1252, Basque (Basque) 0x0029, fa, , , Persian 0x0429, fa_IR, , , Persian , fa_IR, Farsi_Iran.1256, WINDOWS-1256, Farsi (Iran) 0x000B, fi, , , Finnish 0x040B, fi_FI, Finnish_Finland.1252, WINDOWS-1252, Finnish (Finland) 0x0464, fil_PH, Filipino_Philippines.1252, WINDOWS-1252, Filipino (Philippines) 0x0038, fo, , , Faroese 0x0438, fo_FO, , , Faroese (Faroe Islands) 0x000C, fr, , , French 0x080C, fr_BE, , , French (Belgium) 0x0C0C, fr_CA, , , French (Canada) 0x100C, fr_CH, , , French (Switzerland) 0x040C, fr_FR, French_France.1252, WINDOWS-1252, French (France) 0x140C, fr_LU, , , French (Luxembourg) 0x180C, fr_MC, , , French (Principality of Monaco) 0x0462, fy_NL, , , Frisian (Netherlands) , ga, , WINDOWS-1252, Gaelic; Scottish Gaelic 0x083C, ga_IE, , , Irish (Ireland) 0x0056, gl, , , Galician 0x0456, gl_ES, , , Galician (Galician) , gl_ES, Galician_Spain.1252, WINDOWS-1252, Gallego 0x0484, gsw_FR, , , Alsatian (France) 0x0047, gu, , , Gujarati 0x0447, gu_IN, Gujarati_India.0, , Gujarati (India) 0x0468, ha_Latn_NG, , , Hausa (Latin, Nigeria) 0x000D, he, , , Hebrew 0x040D, he_IL, Hebrew_Israel.1255, WINDOWS-1255, Hebrew (Israel) 0x0039, hi, Hindi.65001, , Hindi 0x0439, hi_IN, , , Hindi (India) 0x001A, hr, , , Croatian 0x101A, hr_BA, , , Croatian (Latin, Bosnia and Herzegovina) 0x041A, hr_HR, Croatian_Croatia.1250, WINDOWS-1250, Croatian (Croatia) 0x000E, hu, , , Hungarian 0x040E, hu_HU, Hungarian_Hungary.1250, WINDOWS-1250, Hungarian (Hungary) 0x002B, hy, , , Armenian 0x042B, hy_AM, , , Armenian (Armenia) 0x0021, id, , , Indonesian 0x0421, id_ID, Indonesian_indonesia.1252, WINDOWS-1252, Indonesian (Indonesia) 0x0470, ig_NG, , , Igbo (Nigeria) 0x0478, ii_CN, , , Yi (PRC) 0x000F, is, , , Icelandic 0x040F, is_IS, Icelandic_Iceland.1252, WINDOWS-1252, Icelandic (Iceland) 0x0010, it, , , Italian 0x0810, it_CH, , , Italian (Switzerland) 0x0410, it_IT, Italian_Italy.1252, WINDOWS-1252, Italian (Italy) 0x045D, iu_Cans_CA, , , Inuktitut (Syllabics, Canada) 0x085D, iu_Latn_CA, , , Inuktitut (Latin, Canada) 0x0011, ja, , , Japanese 0x0411, ja_JP, Japanese_Japan.932, CP932, Japanese (Japan) 0x0037, ka, , , Georgian 0x0437, ka_GE, , , Georgian (Georgia) , ka_GE, Georgian_Georgia.65001, , Georgian 0x003F, kk, , , Kazakh 0x043F, kk_KZ, , , Kazakh (Kazakhstan) 0x046F, kl_GL, , , Greenlandic (Greenland) , km, Khmer.65001, , Khmer 0x0453, km_KH, , , Khmer (Cambodia) 0x004B, kn, , , Kannada 0x044B, kn_IN, kn_IN.UTF-8, Kannada.65001, Kannada (India) 0x0012, ko, , , Korean 0x0057, kok, , , Konkani 0x0457, kok_IN, , , Konkani (India) 0x0412, ko_KR, Korean_Korea.949, EUC-KR, Korean (Korea) 0x0040, ky, , , Kyrgyz 0x0440, ky_KG, , , Kyrgyz (Kyrgyzstan) 0x046E, lb_LU, , , Luxembourgish (Luxembourg) 0x0454, lo_LA, Lao_Laos.UTF-8, WINDOWS-1257, Lao (Lao P.D.R.) 0x0027, lt, , , Lithuanian 0x0427, lt_LT, Lithuanian_Lithuania.1257, WINDOWS-1257, Lithuanian (Lithuania) 0x0026, lv, , , Latvian 0x0426, lv_LV, Latvian_Latvia.1257, WINDOWS-1257, Latvian (Latvia) 0x0481, mi_NZ, Maori.1252, WINDOWS-1252, Maori (New Zealand) 0x002F, mk, , , Macedonian 0x042F, mk_MK, , , Macedonian (Former Yugoslav Republic of Macedonia) 0x044C, ml_IN, Malayalam_India.x-iscii-ma, x-iscii-ma, Malayalam (India) 0x0050, mn, , , Mongolian 0x0450, mn_MN, Cyrillic_Mongolian.1251, , Mongolian (Cyrillic, Mongolia) 0x0850, mn_Mong_CN, , , Mongolian (Traditional Mongolian, PRC) 0x047C, moh_CA, , , Mohawk (Mohawk) 0x004E, mr, , , Marathi 0x044E, mr_IN, , , Marathi (India) 0x003E, ms, , , Malay 0x083E, ms_BN, , , Malay (Brunei Darussalam) 0x043E, ms_MY, , , Malay (Malaysia) 0x043A, mt_MT, , , Maltese (Malta) 0x0461, ne_NP, , , Nepali (Nepal) 0x0013, nl, , , Dutch 0x0813, nl_BE, , , Dutch (Belgium) 0x0413, nl_NL, Dutch_Netherlands.1252, WINDOWS-1252, Dutch (Netherlands) 0x0814, nn_NO, Norwegian-Nynorsk_Norway.1252, WINDOWS-1252, Norwegian, Nynorsk (Norway) 0x0014, no, , , Norwegian 0x0414, no_NO, Norwegian_Norway.1252, WINDOWS-1252, Norwegian, Bokmål (Norway) 0x046C, nso_ZA, , , Sesotho sa Leboa (South Africa) 0x0482, oc_FR, , , Occitan (France) 0x0448, or_IN, , , Oriya (India) 0x0046, pa, , , Punjabi 0x0446, pa_IN, , , Punjabi (India) 0x0015, pl, , , Polish 0x0415, pl_PL, Polish_Poland.1250, WINDOWS-1250, Polish (Poland) 0x048C, prs_AF, , , Dari (Afghanistan) 0x0463, ps_AF, , , Pashto (Afghanistan) 0x0016, pt, , , Portuguese 0x0416, pt_BR, Portuguese_Brazil.1252, WINDOWS-1252, Portuguese (Brazil) 0x0816, pt_PT, Portuguese_Portugal.1252, WINDOWS-1252, Portuguese (Portugal) 0x0486, qut_GT, , , K'iche (Guatemala) 0x046B, quz_BO, , , Quechua (Bolivia) 0x086B, quz_EC, , , Quechua (Ecuador) 0x0C6B, quz_PE, , , Quechua (Peru) 0x0417, rm_CH, , , Romansh (Switzerland) 0x0018, ro, , , Romanian 0x0418, ro_RO, Romanian_Romania.1250, WINDOWS-1250, Romanian (Romania) 0x0019, ru, , , Russian 0x0419, ru_RU, Russian_Russia.1251, WINDOWS-1251, Russian (Russia) 0x0487, rw_RW, , , Kinyarwanda (Rwanda) 0x004F, sa, , , Sanskrit 0x0485, sah_RU, , , Yakut (Russia) 0x044F, sa_IN, , , Sanskrit (India) 0x0C3B, se_FI, , , Sami, Northern (Finland) 0x043B, se_NO, , , Sami, Northern (Norway) 0x083B, se_SE, , , Sami, Northern (Sweden) 0x045B, si_LK, , , Sinhala (Sri Lanka) 0x001B, sk, , , Slovak 0x041B, sk_SK, Slovak_Slovakia.1250, WINDOWS-1250, Slovak (Slovakia) 0x0024, sl, , , Slovenian 0x0424, sl_SI, Slovenian_Slovenia.1250, WINDOWS-1250, Slovenian (Slovenia) 0x183B, sma_NO, , , Sami, Southern (Norway) 0x1C3B, sma_SE, , , Sami, Southern (Sweden) 0x103B, smj_NO, , , Sami, Lule (Norway) 0x143B, smj_SE, , , Sami, Lule (Sweden) 0x243B, smn_FI, , , Sami, Inari (Finland) 0x203B, sms_FI, , , Sami, Skolt (Finland) , so_SO, , , Somali (Somalia) 0x001C, sq, , , Albanian 0x041C, sq_AL, Albanian_Albania.1250, WINDOWS-1250, Albanian (Albania) 0x7C1A, sr, , , Serbian 0x1C1A, sr_Cyrl_BA,Serbian (Cyrillic)_Serbia and Montenegro.1251,WINDOWS-1251,Serbian (Cyrillic, Bosnia and Herzegovina) 0x0C1A, sr_Cyrl_SP, , , Serbian (Cyrillic, Serbia) 0x181A, sr_Latn_BA, , , Serbian (Latin, Bosnia and Herzegovina) 0x081A, sr_Latn_SP, , , Serbian (Latin, Serbia) 0x001D, sv, , , Swedish 0x081D, sv_FI, , , Swedish (Finland) 0x041D, sv_SE, Swedish_Sweden.1252, WINDOWS-1252, Swedish (Sweden) 0x0041, sw, , , Kiswahili 0x0441, sw_KE, , , Kiswahili (Kenya) 0x005A, syr, , , Syriac 0x045A, syr_SY, , , Syriac (Syria) 0x0049, ta, , , Tamil 0x0449, ta_IN, , , Tamil (India) 0x004A, te, , , Telugu 0x044A, te_IN, , , Telugu (India) 0x0428, tg_Cyrl_TJ, , , Tajik (Cyrillic, Tajikistan) 0x001E, th, , , Thai 0x041E, th_TH, Thai_Thailand.874, WINDOWS-874, Thai (Thailand) 0x0442, tk_TM, , , Turkmen (Turkmenistan) 0x085F, tmz_Latn_DZ, , , Tamazight (Latin, Algeria) 0x0432, tn_ZA, , , Setswana (South Africa) 0x001F, tr, , , Turkish 0x041F, tr_TR, Turkish_Turkey.1254, WINDOWS-1254, Turkish (Turkey) 0x0044, tt, , , Tatar 0x0444, tt_RU, , , Tatar (Russia) 0x0480, ug_CN, , , Uighur (PRC) 0x0022, uk, , , Ukrainian 0x0422, uk_UA, Ukrainian_Ukraine.1251, WINDOWS-1251, Ukrainian (Ukraine) 0x0020, ur, , , Urdu 0x0420, ur_PK, , , Urdu (Islamic Republic of Pakistan) 0x0043, uz, , , Uzbek 0x0843, uz_Cyrl_UZ, , , Uzbek (Cyrillic, Uzbekistan) 0x0443, uz_Latn_UZ, , , Uzbek (Latin, Uzbekistan) 0x002A, vi, , , Vietnamese 0x042A, vi_VN, Vietnamese_Viet Nam.1258, WINDOWS-1258, Vietnamese (Vietnam) 0x082E, wee_DE, , , Lower Sorbian (Germany) 0x042E, wen_DE, , , Upper Sorbian (Germany) 0x0488, wo_SN, , , Wolof (Senegal) 0x0434, xh_ZA, , , isiXhosa (South Africa) 0x046A, yo_NG, , , Yoruba (Nigeria) 0x0804, zh_CN, Chinese_China.936, CP936, Chinese (People's Republic of China) 0x0004, zh_Hans,, , Chinese (Simplified) 0x7C04, zh_Hant,, , Chinese (Traditional) 0x0C04, zh_HK, , , Chinese (Hong Kong S.A.R.) 0x1404, zh_MO, , , Chinese (Macao S.A.R.) 0x1004, zh_SG, , , Chinese (Singapore) 0x0404, zh_TW, Chinese_Taiwan.950, CP950, Chinese (Taiwan) 0x0435, zu_ZA, , , siZulu (South Africa) Log-Report-Lexicon-1.12/lib/Log/Report/Translator/0000755000175000001440000000000015000465541022437 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/Translator/Gettext.pod0000644000175000001440000000337415000465535024601 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Translator::Gettext - the GNU gettext infrastructure =head1 INHERITANCE Log::Report::Translator::Gettext is a Log::Report::Translator =head1 SYNOPSIS # normal use (end-users view) textdomain 'my-domain' , translator => Log::Report::Translator::Gettext->new; print __"Hello World\n"; # language determined by environment # internal use my $msg = Log::Report::Message->new ( _msgid => "Hello World\n" , _textdomain => 'my-domain' ); print Log::Report::Translator::Gettext->new ->translate($msg, 'nl-BE'); =head1 DESCRIPTION UNTESTED!!! PLEASE CONTRIBUTE!!! Translate a message using the GNU gettext infrastructure. Guido Flohr reports: be aware that Locale::gettext is only a binding for the C library libintl and depends on its features. That means that your module will effectively only run on GNU systems and maybe on Solaris (depending on the exact version), because only these systems provide the plural handling functions ngettext(), dngettext() and dcngettext(). Sooner or later you will probably also need bind_textdomain_codeset() which is also only available on certain systems. Extends L<"DESCRIPTION" in Log::Report::Translator|Log::Report::Translator/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Translator|Log::Report::Translator/"METHODS">. =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Translator/Context.pod0000644000175000001440000003343215000465535024577 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Translator::Context - handle translation contexts =head1 SYNOPSIS # usually, the context information is in a separate file textdomain 'my-domain' , config => $filename; =head1 DESCRIPTION [Added in Log::Report v1.00] The "contexts" concept in (GNU's version of) gettext, has a very restricted purpose: to separate two (accidental) uses of the same message-id, under different circumstances. The same msgid may translated diffently in one file or the other. For instance, two libraries used in the same application, or two componentent within a single library both want to used the same default text (which usually is very short) char * t1 = pgettext('interface', 'None'); char * t2 = pgettext('selections', 'None'); Some translation setups use the library name consequently as msgctxt. But, the name "context" is pretending much more power than the gettext libraries are capable of: it usually only behaves like a namespace. For L, the power of "context" is extended with selecting between alternatives for the use of a msgid B. For instance, the gender of the user of the website determines whether `he' or `she' needs to be used in the translation. In this example, the gender is set as context keyword in the message: my ($name, $sex) = ('Jack', 'male'); print __x"{name $name , _context => "gender=$sex"; =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Translator::Context-EB(%options) -Option--Default rules {} =over 2 =item rules => HASH =back =back =head2 Attributes =over 4 =item $obj-EB() Returns a HASH to the simplified context maps. =back =head2 Action =over 4 =item $obj-EB( $message, $lang, [$context] ) Returns a pair of the MSGID stripped from context markup, and the context evaluated into the msgctxt string. The $message is a L object. The $context is the default context for a certain textdomain. my ($msgid, $msgctxt) = $context->ctxtFor($msg, $lang, $context); =item $obj-EB($msgid, $language, %options) Expand the context settings into all possible combinations which need translations in the PO file. This may depend on the $language. The $msgid is used in error messages. =item $obj-EB($source, STRING|ARRAY|HASH|PAIRS) =item Log::Report::Translator::Context-EB($source, STRING|ARRAY|HASH|PAIRS) Converts the context settings passed with the MSGID, into a HASH which will be matched to the context providers. =back =head1 DETAILS The "contexts" concept in (GNU's version of) gettext, has a very restricted purpose: to separate two (accidental) uses of the same message-id, under different circumstances. The same msgid may translated diffently in one file or the other. For instance, two libraries used in the same application, or two componentent within a single library both want to used the same default text (which usually is very short) char * t1 = pgettext('interface', 'None'); char * t2 = pgettext('selections', 'None'); Some translation setups use the library name consequently as msgctxt. But, the name "context" is pretending much more power than the gettext libraries are capable of: it usually only behaves like a namespace. =head2 Contexts in Log::Report For L, the power of "context" is extended with selecting between alternatives for the use of a msgid B. For instance, the gender of the user of the website determines whether `he' or `she' needs to be used in the translation. In this example, the gender is set as context keyword in the message: my ($name, $sex) = ('Jack', 'male'); print __x"{name $name , _context => "gender=$sex"; This would also be possible in traditional gettext, although probably rarely used. A complication is that the scripts to maintain the po tables are not too smart; do not understand complex code constructs. Probably this would beed needed: if(sex==MALE) { printf pgettext('male', "%s found his key\n", name); } else { printf pgettext('female', "%s found her key\n", name); } =head2 Using context_rules In Log::Report's extended concept of "contexts", you can select between multiple translations for the same msgid, when they =over 4 =item * appear with different purpose (like gnu's concept of contexts) =item * need alternative translation sets B =item * interpolate global parameters in messages =back In the standard gettext set-up, some msgid may accidentally collide between two different uses. For instance, whether you translate the word "Open" in the menu for "Files" to mean "open a file", and the word "Open" in the status display meaning "the file is open". In some languages, these translations may differ. Using a msgctxt keyword will cause the same msgid to appear twice in the PO-file. But, there is a much broader need for context sensitive translations, which is not in the provided by standard gettext: environmental information or parameters may influence the translation more than simply solvable by inserted parameters. For instance, the gender of the user of the website determines whether `he' or `she' needs to be used. In this example, the gender is set as context keyword in the message: $name = 'Jack'; print __x"{name} found her key", name => $name; You may try to solve this via: my ($name, $gender) = ('Jack', 'male'); print __x"{name} found {personal} key", name => $name , personal => ($gender eq 'male' ? 'his' : 'her'); # No! This does not translate! For one, you would need to translate C and C to the language as well. But in some languages, the differences between addressed genders have more impact on the whole sentence. So, Log::Report translations add extra syntax: my ($name, $gender) = ('Jack', 'male'); print __x"{name $name , _context => "gender=$gender"; The C marking tells the translation table builder (xgettext-perl) and the translation handler that there is a context active. Now, the English PO-file has # gender alternatives 'male' and 'female' msgctxt "gender=male" msgid "{name} found his key" msgstr "{name} found his key" msgctxt "gender=female" msgid "{name} found his key" msgstr "{name} found her key" To make this work, both the application and the C script must share information to understand which genders are available. See the section on "Configuration" below. Another example: print __x"greetings{ 'gender=male' _context => 'gender=male,agegroup=adult,married=yes' _context => [ 'gender=male', 'agegroup=adult', 'married=yes'] _context => [ qw/gender=male agegroup=adult married=yes/ ] my @context = (qw/gender=male agegroup=adult married=yes/); _context => \@context; Probably the my %context = (gender => 'male', agegroup => 'adult', married => 'yes'); my %context = qw/gender male agegroup adult married yes/; _context => \%context; Standard gettext only allows a single keyword (=string) C permits you to set-up a context for a whole text-domain, which means that multiple context rules may be active at any moment. =head3 Global parameters You can use contexts to set global interpolation parameters. For instance, running a pure perl webserver, you may serve multiple domains. Some of the log messages may need to show that domain name. Of course, you can collect (or pass on) the hostname when throwing the error... something like this: # can I access $vhost easily? error __x"For {host}, login failed for {user}" , host => $vhost->name, user => $user; Via contexts: # when you know the vhost: (max once per request) textdomain->setContext(host => $vhost->name); # or updateContext # until you reconfigure the context error __x"For {_context.host}, login failed for {user}", user => $user; The context values are always available for interpolation. =head3 Specifying the context per Domain Above examples are to be specified per message. You may also set a default. The top of your modules set the text-domain (name of the translation table) for all strings found in those files. In this case, for instance "webpages" # Log::Report::textdomain() (textdomain 'webpages')->setContext(%context); This context is used as defaults, the C<_context> attribute used by strings are overruling these. =head3 The msgctxt The gnutext implementation of the context is very simple. This is to be expected from a library written in C. The msgctxt alternatives are matched against the context keywords of the message. In all or none of the alternatives match, then just a random translation is chosen. In the simplest form, the msgctxt field contains a single keyword (not containing a comma). msgctxt "gender=male" But you can do more. B that most (all?) existing tools which smartly edit PO-files do not understand these constructs: they see the msgctxt as dump string without meaning. msgctxt "agegroup=baby,agegroup=grandparent" # baby OR grandparent msgctxt "gender=male agegroup=adult" # both male AND adult So, a comma separated list of alternatives. If any matches, then the rule is selected. =head3 Configuration The tools which handle translations expect the msgctxt to be static. For instance, contain a filename where the string is used to disambigue accidental collissions of the use of the same msgid for different purposes. Now, we have designed far more flexible contexts. We need to generate all possible msgctxt values while extracting msgids to update the PO-files. Therefore, we need a map-file. The context maps are included in a configuration file which is passed to xgettext-perl and to the program which uses contexts. See L. Example of such configuration file: (JSON syntax and Perl syntax) === JSON === ==== Perl === { { "context_rules" : { context_rules => { "gender" : [ gender => [ "male", 'male', "female" 'female' ] ] } } } } or { { "context_rules" : { context_rules => { "gender" : { gender => { "alternatives" : [ alternatives => [ "male", 'male', "female", 'female', "unknown" 'unknown' ] ] ... more config for 'gender' ... } } } } } } As "alternatives", we list the alternatives as known by the application internals. Each msgid which contains a C<< {> mark will be replicated three times, in each language table. Each copy will be marked with a different value from "alternatives". However, languages differ. For instance, in some language we may address the C gender as being a male person. In other languages, the translation can express this "unknown" personality. To get this to work, you can use the C construct. The default C, as used in the previous example, is simply mapping the alternatives directly on msgctxt values which are the same: { { context_rules => "context_rules" : { { gender => "gender" : { { default => { qw/ "default" : { female female "female" : "female", male male "male" : "male" unknown male / } "unknown" : "male", , 'nl,de' => { qw/ }, unknown x / } "nl,de" : { "unknown" : "x" } } ... more configuration ... } ... more context rules ... } } } } By default, there will only be two msgid copies in a language file, because at run-time the "unknown" is mapped on "male". An exception for the Dutch (nl*) and German (de*) tables, which apparently support the third gender. If you are not interested for a certain tag, then put it on 'IGNORE' as default or for your language. "default" : "IGNORE", default => 'IGNORE' "nl": "IGNORE" nl => 'IGNORE' =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Translator/POT.pod0000644000175000001440000000653415000465535023620 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Translator::POT - translation based on POT files =head1 INHERITANCE Log::Report::Translator::POT is a Log::Report::Translator =head1 SYNOPSIS # internal use my $msg = Log::Report::Message->new ( _msgid => "Hello World\n" , _domain => 'my-domain' ); print Log::Report::Translator::POT ->new(lexicons => $dir) ->translate($msg, 'nl-BE'); # normal use (end-users view in the program's ::main) textdomain 'my-domain' , translator => Log::Report::Translator::POT->new(lexicon => $dir); print __"Hello World\n"; =head1 DESCRIPTION Translate a message by directly accessing POT files. The files will load lazily (unless forced). This module accesses the PO's in a compact way, using L, which is much more efficient than L. Extends L<"DESCRIPTION" in Log::Report::Translator|Log::Report::Translator/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Translator|Log::Report::Translator/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Translator|Log::Report::Translator/"Constructors">. =over 4 =item Log::Report::Translator::POT-EB(%options) -Option --Default charset lexicons =over 2 =item charset => STRING Enforce character set for files. We default to reading the character-set as defined in the header of each PO file. =item lexicons => DIRECTORY The DIRECTORY where the translations can be found. See L for the expected structure of such DIRECTORY. The default is based on the location of the module which instantiates this translator. The filename of the module is stripped from its C<.pm> extension, and used as directory name. Within that directory, there must be a directory named C, which will be the root directory of a L. =back example: default lexicon directory # file xxx/perl5.8.8/My/Module.pm use Log::Report 'my-domain' , translator => Log::Report::Translator::POT->new; # lexicon now in xxx/perl5.8.8/My/Module/messages/ =back =head2 Accessors Extends L<"Accessors" in Log::Report::Translator|Log::Report::Translator/"Accessors">. =over 4 =item $obj-EB() Returns the default charset, which can be overrule by the locale. =item $obj-EB() Returns a list of L objects, where the translation files may be located. =back =head2 Translating Extends L<"Translating" in Log::Report::Translator|Log::Report::Translator/"Translating">. =over 4 =item $obj-EB($domain, $locale) Inherited, see L =item $obj-EB( $message, [$language, $ctxt] ) Inherited, see L =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Translator/Context.pm0000644000175000001440000001011615000465534024422 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Translator::Context;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; sub new(@) { my $class = shift; (bless {}, $class)->init({@_}) } sub init($) { my ($self, $args) = @_; $self->{LRTC_rules} = $self->_context_table($args->{rules} || {}); $self; } #------- sub rules() {shift->{LRTC_rules}} #------- sub _strip_ctxt_spec($) { my $msgid = shift; my @tags; while($msgid =~ s/\{ ([^<}]*) \<(\w+) ([^}]*) \}/ length "$1$3" ? "{$1$3}" : ''/xe) { push @tags, $2; } ($msgid, [sort @tags]); } sub ctxtFor($$;$) { my ($self, $msg, $lang, $def_context) = @_; my $rawid = $msg->msgid; my ($msgid, $tags) = _strip_ctxt_spec $rawid; @$tags or return ($msgid, undef); my $maps = $self->rules; $lang =~ s/_.*//; my $msg_context = $self->needDecode($rawid, $msg->context || {}); $def_context ||= {}; #use Data::Dumper; #warn "context = ", Dumper $msg, $msg_context, $def_context; my @c; foreach my $tag (@$tags) { my $map = $maps->{$tag} or error __x"no context definition for `{tag}' in `{msgid}'" , tag => $tag, msgid => $rawid; my $set = $map->{$lang} || $map->{default}; next if $set eq 'IGNORE'; my $v = $msg_context->{$tag} || $def_context->{$tag}; unless($v) { warning __x"no value for tag `{tag}' in the context", tag => $tag; ($v) = keys %$set; } unless($set->{$v}) { warning __x"unknown alternative `{alt}' for tag `{tag}' in context of `{msgid}'" , alt => $v, tag => $tag, msgid => $rawid; ($v) = keys %$set; } push @c, "$tag=$set->{$v}"; } my $msgctxt = join ' ', sort @c; ($msgid, $msgctxt); } sub needDecode($@) { my ($thing, $source) = (shift, shift); return +{@_} if @_ > 1; my $c = shift; return $c if !defined $c || ref $c eq 'HASH'; my %c; foreach (ref $c eq 'ARRAY' ? @$c : (split /[\s,]+/, $c)) { my ($kw, $val) = split /\=/, $_, 2; defined $val or error __x"tags value must have form `a=b', found `{this}' in `{source}'" , this => $_, source => $source; $c{$kw} = $val; } \%c; } sub expand($$@) { my ($self, $raw, $lang) = @_; my ($msgid, $tags) = _strip_ctxt_spec $raw; $lang =~ s/_.*//; my $maps = $self->rules; my @options = []; foreach my $tag (@$tags) { my $map = $maps->{$tag} or error __x"unknown context tag '{tag}' used in '{msgid}'" , tag => $tag, msgid => $msgid; my $set = $map->{$lang} || $map->{default}; my %uniq = map +("$tag=$_" => 1), values %$set; my @oldopt = @options; @options = (); foreach my $alt (keys %uniq) { push @options, map +[ @$_, $alt ], @oldopt; } } ($msgid, [sort map join(' ', @$_), @options]); } sub _context_table($) { my ($self, $rules) = @_; my %rules; foreach my $tag (keys %$rules) { my $d = $rules->{$tag}; $d = +{ alternatives => $d } if ref $d eq 'ARRAY'; my %simple; my $default = $d->{default} || {}; # default map if(my $alt = $d->{alternatives}) # simpelest map { $default = +{ map +($_ => $_), @$alt }; } $simple{default} = $default; foreach my $set (keys %$d) { next if $set eq 'default' || $set eq 'alternatives'; my %set = (%$default, %{$d->{$set}}); $simple{$_} = \%set for split /\,/, $set; # table per lang } $rules{$tag} = \%simple; } \%rules; } #------------ 1; Log-Report-Lexicon-1.12/lib/Log/Report/Translator/POT.pm0000644000175000001440000000641615000465534023450 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Translator::POT;{ our $VERSION = '1.12'; } use base 'Log::Report::Translator'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use Log::Report::Lexicon::Index; use Log::Report::Lexicon::POTcompact; use POSIX qw/:locale_h/; use Scalar::Util qw/blessed/; use File::Spec (); my %lexicons; sub _fn_to_lexdir($); # Work-around for missing LC_MESSAGES on old Perls and Windows { no warnings; eval "&LC_MESSAGES"; *LC_MESSAGES = sub(){5} if $@; } sub new(@) { my $class = shift; # Caller cannot wait until init() $class->SUPER::new(callerfn => (caller)[1], @_); } sub init($) { my ($self, $args) = @_; $self->SUPER::init($args); my $lex = delete $args->{lexicons} || delete $args->{lexicon} || (ref $self eq __PACKAGE__ ? [] : _fn_to_lexdir $args->{callerfn}); error __x"You have to upgrade Log::Report::Lexicon to at least 1.00" if +($Log::Report::Lexicon::Index::VERSION || 999) < 1.00; my @lex; foreach my $dir (ref $lex eq 'ARRAY' ? @$lex : $lex) { # lexicon indexes are shared my $l = $lexicons{$dir} ||= Log::Report::Lexicon::Index->new($dir); $l->index; # index the files now push @lex, $l; } $self->{LRTP_lexicons} = \@lex; $self->{LRTP_charset} = $args->{charset}; $self; } sub _fn_to_lexdir($) { my $fn = shift; $fn =~ s/\.pm$//; File::Spec->catdir($fn, 'messages'); } #------------ sub lexicons() { @{shift->{LRTP_lexicons}} } sub charset() { shift->{LRTP_charset} } #------------ sub translate($;$$) { my ($self, $msg, $lang, $ctxt) = @_; #!!! do not debug with $msg in a print: recursion my $domain = $msg->{_domain}; my $dname = blessed $domain ? $domain->name : $domain; my $locale = $lang || setlocale(LC_MESSAGES) or return $self->SUPER::translate($msg, $lang, $ctxt); my $pot = exists $self->{LRTP_pots}{$dname}{$locale} ? $self->{LRTP_pots}{$dname}{$locale} : $self->load($dname, $locale); ($pot ? $pot->msgstr($msg->{_msgid}, $msg->{_count}, $ctxt) : undef) || $self->SUPER::translate($msg, $lang, $ctxt); } sub load($$) { my ($self, $dname, $locale) = @_; foreach my $lex ($self->lexicons) { my $fn = $lex->find($dname, $locale); !$fn && $lex->list($dname) and last; # there are tables for dname, but not our lang $fn or next; my ($ext) = lc($fn) =~ m/\.(\w+)$/; my $class = $ext eq 'mo' ? 'Log::Report::Lexicon::MOTcompact' : $ext eq 'po' ? 'Log::Report::Lexicon::POTcompact' : error __x"unknown translation table extension '{ext}' in {filename}", ext => $ext, filename => $fn; info __x"read table {filename} as {class} for {dname} in {locale}", filename => $fn, class => $class, dname => $dname, locale => $locale if $dname ne 'log-report'; # avoid recursion eval "require $class" or panic $@; return $self->{LRTP_pots}{$dname}{$locale} = $class->read($fn, charset => $self->charset); } $self->{LRTP_pots}{$dname}{$locale} = undef; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Translator/Gettext.pm0000644000175000001440000000225115000465534024423 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Translator::Gettext;{ our $VERSION = '1.12'; } use base 'Log::Report::Translator'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use Locale::gettext; sub translate($;$$) { my ($msg, $lang, $ctxt) = @_; #XXX MO: how to use $lang when specified? my $domain = $msg->{_textdomain}; load_domain $domain; my $count = $msg->{_count}; defined $count ? ( defined $msg->{_category} ? dcngettext($domain, $msg->{_msgid}, $msg->{_plural}, $count , $msg->{_category}) : dngettext($domain, $msg->{_msgid}, $msg->{_plural}, $count) ) : ( defined $msg->{_category} ? dcgettext($domain, $msg->{_msgid}, $msg->{_category}) : dgettext($domain, $msg->{_msgid}) ); } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Extract/0000755000175000001440000000000015000465541021720 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/Extract/PerlPPI.pod0000644000175000001440000001167715000465535023716 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Extract::PerlPPI - Collect translatable strings from Perl using PPI =head1 INHERITANCE Log::Report::Extract::PerlPPI is a Log::Report::Extract =head1 SYNOPSIS my $ppi = Log::Report::Extract::PerlPPI->new ( lexicon => '/usr/share/locale' ); $ppi->process('lib/My/Pkg.pm'); # call for each .pm file $ppi->showStats; # optional $ppi->write; # See script xgettext-perl bin/xgettext-perl -p $lexdir @source_dirs =head1 DESCRIPTION This module helps maintaining the POT files, updating the list of message-ids which are kept in them. After initiation, the L method needs to be called with all files which changed since last processing and the existing PO files will get updated accordingly. If no translations exist yet, one C<$lexicon/$domain.po> file will be created. If you want to start a translation, copy C<$lexicon/$domain.po> to C<$lexicon/$domain/$lang.po> and edit that file. You may use C to edit po-files. There are many smart translation management applications which can hand po-files, for instance Pootle and Weblate. Do not forget to add the new po-file to your distribution (MANIFEST) Extends L<"DESCRIPTION" in Log::Report::Extract|Log::Report::Extract/"DESCRIPTION">. =head2 The extraction process All pm-files need to be processed in one go: no incremental processing! The Perl source is parsed using PPI, which does understand Perl syntax quite well, but does not support all features. Automatically, the textdomain of the translations is discovered, as first parameter of C. You may switch textdomain inside one pm-file. When all files have been processed, during the L, all existing po-files for all discovered textdomains will get updated. Not only the C<$lexicon/$domain.po> template, but also all C<$lexicon/$domain/$lang.po> will be replaced. When a msgid has disappeared, existing translations will get disabled, not removed. New msgids will be added and flagged "fuzzy". =head3 What is extracted? This script will extract the msgids used in C<__()>, C<__x()>, C<__xn()>, and C<__n()> (implemented by L) For instance __x"msgid", @more __x'msgid', @more <--- no! syntax error! __x("msgid", @more) __x('msgid', @more) __x(msgid => @more) Besides, there are some helpers which are no-ops in the code, only to fill the po-tables: C, C, C =head3 What is not extracted? B extracted are the usage of anything above, where the first parameter is not a simple string. Not extracted are __x($format, @more) __x$format, @more __x(+$format, _domain => 'other domain', @more) __x($first.$second, @more) In these cases, you have to use C functions to declare the possible values of C<$format>. =head1 METHODS Extends L<"METHODS" in Log::Report::Extract|Log::Report::Extract/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Extract|Log::Report::Extract/"Constructors">. =over 4 =item Log::Report::Extract::PerlPPI-EB(%options) Inherited, see L =back =head2 Accessors Extends L<"Accessors" in Log::Report::Extract|Log::Report::Extract/"Accessors">. =over 4 =item $obj-EB($domain, $pot, %options) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB($domain) Inherited, see L =back =head2 Processors Extends L<"Processors" in Log::Report::Extract|Log::Report::Extract/"Processors">. =over 4 =item $obj-EB(%options) Inherited, see L =item $obj-EB($filename, %options) Update the domains mentioned in the $filename. All textdomains defined in the file will get updated automatically, but not written before all files where processed. -Option --Default charset 'iso-8859-1' =over 2 =item charset => STRING =back =item $obj-EB( [$domains] ) Inherited, see L =item $obj-EB( $domain, $filename, $linenr, $context, $msg, [$msg_plural] ) Inherited, see L =item $obj-EB( [$domain] ) Inherited, see L =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Extract/Template.pm0000644000175000001440000001135415000465534024037 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Extract::Template;{ our $VERSION = '1.12'; } use base 'Log::Report::Extract'; use warnings; use strict; use Log::Report 'log-report-lexicon'; sub init($) { my ($self, $args) = @_; $self->SUPER::init($args); $self->{LRET_domain} = $args->{domain} or error "template extract requires explicit domain"; $self->{LRET_pattern} = $args->{pattern}; $self; } #---------- sub domain() {shift->{LRET_domain}} sub pattern() {shift->{LRET_pattern}} #---------- sub process($@) { my ($self, $fn, %opts) = @_; my $charset = $opts{charset} || 'utf-8'; info __x"processing file {fn} in {charset}", fn=> $fn, charset => $charset; my $pattern = $opts{pattern} || $self->pattern or error __"need pattern to scan for, either via new() or process()"; # Slurp the whole file local *IN; open IN, "<:encoding($charset)", $fn or fault __x"cannot read template from {fn}", fn => $fn; undef $/; my $text = ; close IN; my $domain = $self->domain; $self->_reset($domain, $fn); if(ref $pattern eq 'CODE') { return $pattern->($fn, \$text); } elsif($pattern =~ m/^TT([12])-(\w+)$/) { return $self->scanTemplateToolkit($1, $2, $fn, \$text); } else { error __x"unknown pattern {pattern}", pattern => $pattern; } (); } sub _no_escapes_in($$$$) { my ($msgid, $plural, $fn, $linenr) = @_; return if $msgid !~ /\&\w+\;/ && (defined $plural ? $plural !~ /\&\w+\;/ : 1); $msgid .= "|$plural" if defined $plural; warning __x"msgid '{msgid}' contains html escapes, don't do that. File {fn} line {linenr}" , msgid => $msgid, fn => $fn, linenr => $linenr; } sub scanTemplateToolkit($$$$) { my ($self, $version, $function, $fn, $textref) = @_; # Split the whole file on the pattern in four fragments per match: # (text, leading, needed trailing, text, leading, ...) # f.i. ('', '[% loc("', 'some-msgid', '", params) %]', ' more text') my @frags = $version==1 ? split(/[\[%]%(.*?)%[%\]]/s, $$textref) : split(/\[%(.*?)%\]/s, $$textref); my $domain = $self->domain; my $linenr = 1; my $msgs_found = 0; # pre-compile the regexes, for performance my $pipe_func_block = qr/^\s*(?:\|\s*|FILTER\s+)$function\b/; my $msgid_pipe_func = qr/^\s*(["'])([^\r\n]+?)\1\s*\|\s*$function\b/; my $func_msgid_multi = qr/(\b$function\s*\(\s*)(["'])([^\r\n]+?)\2/s; while(@frags > 2) { my ($skip_text, $take) = (shift @frags, shift @frags); $linenr += $skip_text =~ tr/\n//; if($take =~ $pipe_func_block) { # [% | loc(...) %] $msgid [%END%] or [% FILTER ... %]...[% END %] if(@frags < 2 || $frags[1] !~ /^\s*END\s*$/) { error __x"template syntax error, no END in {fn} line {line}" , fn => $fn, line => $linenr; } my $msgid = $frags[0]; # next content my $plural = $msgid =~ s/\|(.*)// ? $1 : undef; _no_escapes_in $msgid, $plural, $fn, $linenr; $self->store($domain, $fn, $linenr, $msgid, $plural); $msgs_found++; $linenr += $take =~ tr/\n//; next; } if($take =~ $msgid_pipe_func) { # [% $msgid | loc(...) %] my $msgid = $2; my $plural = $msgid =~ s/\|(.*)// ? $1 : undef; _no_escapes_in $msgid, $plural, $fn, $linenr; $self->store($domain, $fn, $linenr, $msgid, $plural); $msgs_found++; $linenr += $take =~ tr/\n//; next; } # loc($msgid, ...) form, can appear more than once my @markup = split $func_msgid_multi, $take; while(@markup > 4) { # quads with text, call, quote, msgid $linenr += ($markup[0] =~ tr/\n//) + ($markup[1] =~ tr/\n//); my $msgid = $markup[3]; my $plural = $msgid =~ s/\|(.*)// ? $1 : undef; _no_escapes_in $msgid, $plural, $fn, $linenr; $self->store($domain, $fn, $linenr, $msgid, $plural); $msgs_found++; splice @markup, 0, 4; } $linenr += $markup[-1] =~ tr/\n//; # rest of container } # $linenr += $frags[-1] =~ tr/\n//; # final page fragment not needed $msgs_found; } #---------------------------------------------------- 1; Log-Report-Lexicon-1.12/lib/Log/Report/Extract/PerlPPI.pm0000644000175000001440000001451615000465534023542 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Extract::PerlPPI;{ our $VERSION = '1.12'; } use base 'Log::Report::Extract'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use PPI; # See Log::Report translation markup functions my %msgids = # MSGIDs COUNT OPTS VARS SPLIT ( __ => [1, 0, 0, 0, 0] , __x => [1, 0, 1, 1, 0] , __xn => [2, 1, 1, 1, 0] , __nx => [2, 1, 1, 1, 0] , __n => [2, 1, 1, 0, 0] , N__ => [1, 0, 1, 1, 0] # may be used with opts/vars , N__n => [2, 0, 1, 1, 0] # idem , N__w => [1, 0, 0, 0, 1] ); my $quote_mistake; { my @q = map quotemeta, keys %msgids; local $" = '|'; $quote_mistake = qr/^(?:@q)\'/; } sub process($@) { my ($self, $fn, %opts) = @_; my $charset = $opts{charset} || 'iso-8859-1'; # $charset eq 'iso-8859-1' # or error __x"PPI only supports iso-8859-1 (latin-1) on the moment"; my $doc = PPI::Document->new($fn, readonly => 1) or fault __x"cannot read perl from file {filename}", filename => $fn; my @childs = $doc->schildren; if(@childs==1 && ref $childs[0] eq 'PPI::Statement') { info __x"no Perl in file {filename}", filename => $fn; return 0; } info __x"processing file {fn} in {charset}", fn=> $fn, charset => $charset; my ($pkg, $include, $domain, $msgs_found) = ('main', 0, undef, 0); NODE: foreach my $node ($doc->schildren) { if($node->isa('PPI::Statement::Package')) { $pkg = $node->namespace; # special hack needed for module Log::Report itself if($pkg eq 'Log::Report') { ($include, $domain) = (1, 'log-report'); $self->_reset($domain, $fn); } else { ($include, $domain) = (0, undef) } next NODE; } # Take domains which are as first parameter after 'use Log::Report' if($node->isa('PPI::Statement::Include')) { $node->type eq 'use' or next NODE; my $module = $node->module; $module eq 'Log::Report' || $module eq 'Dancer2::Plugin::LogReport' or next NODE; $include++; my $dom = ($node->schildren)[2]; $domain = $dom->isa('PPI::Token::Quote') ? $dom->string : $dom->isa('PPI::Token::QuoteLike::Words') ? ($dom->literal)[0] : undef; $self->_reset($domain, $fn) if defined $domain; } $node->find_any( sub { # look for the special translation markers $_[1]->isa('PPI::Token::Word') or return 0; my $node = $_[1]; my $word = $node->content; if($word =~ $quote_mistake) { warning __x"use double quotes not single, in {string} on {file} line {line}" , string => $word, fn => $fn, line => $node->location->[0]; return 0; } my $def = $msgids{$word} # get __() description or return 0; # Avoid the declaration of the conversion routines in Log::Report $domain ne 'log-report' || ! $node->parent->isa('PPI::Statement::Sub') or return 0; my @msgids = $self->_get($node, $domain, $word, $def) or return 0; my ($nr_msgids, $has_count, $has_opts, $has_vars,$do_split) = @$def; my $line = $node->location->[0]; unless($domain) { mistake __x"no text-domain for translatable at {fn} line {line}", fn => $fn, line => $line; return 0; } my @records = $do_split ? (map +[$_], map {split} @msgids) # Bulk conversion strings : \@msgids; $msgs_found += @records; $self->store($domain, $fn, $line, @$_) for @records; 0; # don't collect }); } $msgs_found; } sub _get($$$$) { my ($self, $node, $domain, $function, $def) = @_; my ($nr_msgids, $has_count, $opts, $vars, $split) = @$def; my $list_only = ($nr_msgids > 1) || $has_count || $opts || $vars; my $expand = $opts || $vars; my @msgids; my $first = $node->snext_sibling; $first = $first->schild(0) if $first->isa('PPI::Structure::List'); $first = $first->schild(0) if $first->isa('PPI::Statement::Expression'); my $line; while(defined $first && $nr_msgids > @msgids) { my $msgid; my $next = $first->snext_sibling; my $sep = $next && $next->isa('PPI::Token::Operator') ? $next : ''; $line = $first->location->[0]; if($first->isa('PPI::Token::Quote')) { last if $sep !~ m/^ (?: | \=\> | [,;:] ) $/x; $msgid = $first->string; if( $first->isa("PPI::Token::Quote::Double") || $first->isa("PPI::Token::Quote::Interpolate")) { mistake __x "do not interpolate in msgid (found '{var}' in line {line})" , var => $1, line => $line if $first->string =~ m/(? $line, error => $@ if $@; } } elsif($first->isa('PPI::Token::Word')) { last if $sep ne '=>'; $msgid = $first->content; } else {last} mistake __x "new-line is added automatically (found in line {line})" , line => $line if !$split && $msgid =~ s/(?snext_sibling; } @msgids or return (); my $next = $first->snext_sibling; if($has_count && !$next) { error __x"count missing in {function} in line {line}" , function => $function, line => $line; } @msgids; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Extract/Template.pod0000644000175000001440000002000115000465535024173 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Extract::Template - collect translatable strings from template files =head1 INHERITANCE Log::Report::Extract::Template is a Log::Report::Extract =head1 SYNOPSIS # First use of this module: extract msgids from various kinds # of text-files, usually web templates. # See script "xgettext-perl" for standard wrapper script my $extr = Log::Report::Extract::Template->new ( lexicon => '/usr/share/locale' , domain => 'my-web-site' , pattern => 'TT2-loc' ); $extr->process('website/page.html'); # many times $extr->showStats; $extr->write; # Second use: connect to Template::Toolkit # See DETAILS chapter below [% loc("Greetings {name},", name => client.name) %] [% | loc(name => client.name) %]Greetings {name}[% END %] [% 'Greetings {name}' | loc(name => client.name) %] =head1 DESCRIPTION This module helps maintaining the POT files which list translatable strings from template files (or other flat text files) by updating the list of message-ids which are kept in them. After initiation, the L method needs to be called for each file in the domain and the existing PO files will get updated accordingly. If no translations exist yet, one C<$textdomain.po> file will be created as point to start. Copy that file into C<$textdomain/$lang.po> Extends L<"DESCRIPTION" in Log::Report::Extract|Log::Report::Extract/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Extract|Log::Report::Extract/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Extract|Log::Report::Extract/"Constructors">. =over 4 =item Log::Report::Extract::Template-EB(%options) -Option --Defined in --Default charset Log::Report::Extract 'utf-8' domain lexicon Log::Report::Extract pattern =over 2 =item charset => STRING =item domain => DOMAIN There is no syntax for specifying domains in templates (yet), so you must be explicit about the collection we are making now. =item lexicon => DIRECTORY =item pattern => PREDEFINED|CODE See the DETAILS section below for a detailed explenation. =back =back =head2 Accessors Extends L<"Accessors" in Log::Report::Extract|Log::Report::Extract/"Accessors">. =over 4 =item $obj-EB($domain, $pot, %options) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB() =item $obj-EB() Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB() =item $obj-EB($domain) Inherited, see L =back =head2 Processors Extends L<"Processors" in Log::Report::Extract|Log::Report::Extract/"Processors">. =over 4 =item $obj-EB(%options) Inherited, see L =item $obj-EB($filename, %options) Update the domains mentioned in the $filename. All textdomains defined in the file will get updated automatically, but not written before all files where processed. -Option --Default charset 'utf-8' pattern =over 2 =item charset => STRING The character encoding used in this template file. =item pattern => PREDEFINED|CODE Read the DETAILS section about this. =back =item $obj-EB( [$domains] ) Inherited, see L =item $obj-EB( $domain, $filename, $linenr, $context, $msg, [$msg_plural] ) Inherited, see L =item $obj-EB( [$domain] ) Inherited, see L =back =head1 DETAILS =head2 Scan Patterns Various template systems use different conventions for denoting strings to be translated. =head3 Predefined for Template-Toolkit There is not a single convention for translations in C (see Template), so you need to specify which version TT you use and which function name you want to use. In extreme cases, you may even build separate translation tables by simply providing using functions. For instance pattern => 'TT2-loc' will scan for [% loc("msgid", key => value, ...) %] [% loc('msgid', key => value, ...) %] [% loc("msgid|plural", count, key => value, ...) %] [% INCLUDE title = loc('something') %] [% | loc(n => name) %]hi {n}[% END %] [% 'hi {n}' | loc(n => name) %] For TT1, the brackets can either be '[%...%]' or '%%...%%'. The function name is treated case-sensitive. Some people prefer 'l()' or 'L()'. The code needed # during initiation of the webserver, once in your script (before fork) my $lexicons = 'some-directory-for-translation-tables'; my $translator = Log::Report::Translator::POT->new(lexicons => $lexicons); my $domain = textdomain $textdomain; $domain->configure(translator => $translator); # your standard template driver sub handler { ... my $vars = { ...all kinds of values... }; $vars->{loc} = \&translate; # <--- this is extra my $output = ''; my $templater = Template->new(...); $templater->process($template_fn, $vars, \$output); print $output; } # anywhere in the same file sub translate { my $textdomain = ...; # your choice when running xgettext-perl my $lang = ...; # how do you figure that out? my $msg = Log::Report::Message->fromTemplateToolkit($textdomain, @_); $msg->toString($lang); } To generate the pod tables, run in the shell something like xgettext-perl -p $lexicons --template TT2-loc \ --domain $textdomain $templates_dir If you want to implement your own extractor --to avoid C-- you need to run something like this: my $extr = Log::Report::Extract::Template->new ( lexicon => $output , charset => 'utf-8' , domain => $domain , pattern => 'TT2-loc' ); $extr->process($_) for @filenames; $extr->write; =head2 Use in combination with contexts This example extends the previous with using context sensitive translations, as implemented by L. Let's say that the translation of some of the sentences on the website depend on the gender of the addressed person. An example of the use in a TT2 template: [% loc("{name person.name) %] The extraction script F will expand this into two records in the PO file, respectively with msgctxt attribute 'gender=male' and 'gender=female'. When your PO-files are not generated by 'xgettext-perl', you do not need a separate domain configuration file: $domain->configure ( context_rules => +{gender => ['male','female']} , translator => $translator ); When your PO-files are generated by 'xgettext-perl', you need to share the context-rules between that msgid extractor and your runtime code. That same file needs to be passed with the 'domain' parameter to the script. # add context_rules either explicit or via 'config' filename $domain->configure ( config => 'my/own/$domain.conf' , translator => $translator ); Now, when you generate the pages, you need to set-up the right context. In this case, we set-up the gender of the person who gets addressed. (The name 'gender' is good for examples, but quite non-descriptive. Maybe 'user_gender' is more maintainable) $domain->setContext( +{gender => 'male'} ); # or ('gender=male') $domain->setContext( "gender=male" ); # same =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/0000755000175000001440000000000015000465541021707 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/POT.pod0000644000175000001440000002045015000465535023061 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::POT - manage PO files =head1 INHERITANCE Log::Report::Lexicon::POT is a Log::Report::Lexicon::Table =head1 SYNOPSIS # this is usually not for end-users, See ::Extract::PerlPPI # using a PO table my $pot = Log::Report::Lexicon::POT ->read('po/nl.po', charset => 'utf-8') or die; my $po = $pot->msgid('msgid'); my $po = $pot->msgid($msgid, $msgctxt); print $pot->nrPlurals; print $pot->msgstr('msgid', 3); print $pot->msgstr($msgid, 3, $msgctxt); $pot->write; # update the file # fill the table, by calling the next a lot my $po = Log::Report::Lexicon::PO->new(...); $pot->add($po); # creating a PO table $pot->write('po/nl.po') or die; =head1 DESCRIPTION This module is reading, extending, and writing POT files. POT files are used to store translations in humanly readable format for most of existing translation frameworks, like GNU gettext and Perl's Maketext. If you only wish to access the translation, then you may use the much more efficient L. The code is loosely based on Locale::PO, by Alan Schwartz. The coding style is a bit off the rest of C, and there was a need to sincere simplification. Each PO record will be represented by a L. Extends L<"DESCRIPTION" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Constructors">. =over 4 =item Log::Report::Lexicon::POT-EB(%options) Create a new POT file. The initial header is generated for you, but it can be changed using the L method. -Option --Default charset 'UTF-8' date now filename undef index {} nr_plurals 2 plural_alg n!=1 plural_forms textdomain version undef =over 2 =item charset => STRING The charset to be used for the createed file. It is unwise to use anything else than 'UTF-8', but allowed. Before [1.09] this option was required. =item date => STRING Overrule the date which is included in the generated header. =item filename => STRING Specify an output filename. The name can also be specified when L is called. =item index => HASH A set of translations (L objects), with msgid as key. =item nr_plurals => INTEGER The number of translations each of the translation with plural form need to have. =item plural_alg => EXPRESSION The algorithm to be used to calculate which translated msgstr to use. =item plural_forms => RULE [0.992] When this option is used, it overrules C and C. The RULE should be a full "Plural-Forms" field. =item textdomain => STRING The package name, used in the directory structure to store the PO files. =item version => STRING =back =item Log::Report::Lexicon::POT-EB($filename, %options) Read the POT information from $filename. -Option --Default charset =over 2 =item charset => STRING The character-set which is used for the file. You must specify this explicitly. =back =item $obj-EB( [$filename|$fh], %options ) When you pass an open $fh, you are yourself responsible that the correct character-encoding (binmode) is set. When the write followed a L or the filename was explicitly set with L, then you may omit the first parameter. -Option --Default only_active false =over 2 =item only_active => BOOLEAN [1.02] Do not write records which do have a translation, but where the msgid has disappeared from the sources. By default, these records are commented out (marked with '#~') but left in the file. =back =back =head2 Attributes Extends L<"Attributes" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Attributes">. =over 4 =item $obj-EB() The character-set to be used for reading and writing. You do not need to be aware of Perl's internal encoding for the characters. =item $obj-EB() Returns the $filename, as derived from L or specified during initiation with L. =item $obj-EB() Returns a HASH of all defined PO objects, organized by msgid. Please try to avoid using this: use L for lookup and L for adding translations. =item $obj-EB() Returns the language code, which is derived from the filename. =back =head2 Managing PO's Extends L<"Managing PO's" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Managing PO's">. =over 4 =item $obj-EB($table) Remove all references which are not found as key in the hash $table. Returns the number of references left. =item $obj-EB($filename) Remove all the references to the indicate $filename from all defined translations. Returns the number of refs left. =item $obj-EB() Returns a HASH with some statistics about this POT table. =item $obj-EB( [$date] ) Replace the "PO-Revision-Date" with the specified $date, or the current moment. =back =head3 Translation Extends L<"Translation" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Translation">. =over 4 =item $obj-EB( STRING, [$msgctxt] ) Lookup the L with the STRING. If you want to add a new translation, use L. Returns C when not defined. =item $obj-EB( $msgid, [$count, [$msgctxt]] ) Returns the translated string for $msgid. When $count is not specified or C, the translation string related to "1" is returned. =back =head3 Administration Extends L<"Administration" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Administration">. =over 4 =item $obj-EB($po) Add the information from a $po into this POT. If the msgid of the $po is already known, that is an error. =item $obj-EB
( [$field, [$content]] ) The translation of a blank MSGID is used to store a MIME header, which contains some meta-data. When only a $field is specified, that content is looked-up (case-insensitive) and returned. When a $content is specified, the knowledge will be stored. In latter case, the header structure may get created. When the $content is set to C, the field will be removed. =item $obj-EB() Inherited, see L =item $obj-EB($count) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB( [$active] ) Returns a list with all defined L objects. When the string C<$active> is given as parameter, only objects which have references are returned. =back =head1 DIAGNOSTICS =over 4 =item Error: no filename or file-handle specified for PO When a PO file is written, then a filename or file-handle must be specified explicitly, or set beforehand using the L method, or known because the write follows a L of the file. =item Error: only acceptable parameter is 'ACTIVE' =item Error: textdomain parameter is required =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/Index.pm0000644000175000001440000000775215000465534023331 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::Index;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; use Log::Report::Util qw/parse_locale/; use File::Find (); # The next two need extension when other lexicon formats are added sub _understand_file_format($) { $_[0] =~ qr/\.(?:gmo|mo|po)$/i } sub _find($$) { my ($index, $name) = (shift, lc shift); $index->{"$name.mo"} || $index->{"$name.gmo"} || $index->{"$name.po"}; # prefer mo } # On windows, other locale names are used. They will get translated # into the Linux (ISO) convensions. my $locale_unifier; if($^O eq 'MSWin32') { require Log::Report::Win32Locale; Log::Report::Win32Locale->import; $locale_unifier = sub { iso_locale($_[0]) }; } else { # some UNIXes do not understand "POSIX" $locale_unifier = sub { uc $_[0] eq 'POSIX' ? 'c' : lc $_[0] }; } sub new($;@) { my ($class, $dir) = (shift, shift); bless {dir => $dir, @_}, $class; # dir before first argument. } #------------------- sub directory() { $_[0]->{dir} } #------------------- sub index() { my $self = shift; return $self->{index} if exists $self->{index}; my $dir = $self->directory; my $strip_dir = qr!\Q$dir/!; $self->{index} = {}; File::Find::find ( +{ wanted => sub { -f && !m[/\.] && _understand_file_format($_) or return 1; (my $key = $_) =~ s/$strip_dir//; $self->addFile($key, $_); 1; } , follow => 1 , no_chdir => 1 , follow_skip => 2 } , $dir ); $self->{index}; } sub addFile($;$) { my ($self, $base, $abs) = @_; $abs ||= File::Spec->catfile($self->directory, $base); $base =~ s!\\!/!g; # dos->unix $self->{index}{lc $base} = $abs; } sub find($$) { my $self = shift; my $domain = lc shift; my $locale = $locale_unifier->(shift); my $index = $self->index; keys %$index or return undef; my ($lang, $terr, $cs, $modif) = parse_locale $locale; unless(defined $lang) { defined $locale or $locale = ''; # avoid problem with recursion, not translatable! print STDERR "illegal locale $locale, when looking for $domain"; return undef; } $terr = defined $terr ? '_'.$terr : ''; $cs = defined $cs ? '.'.$cs : ''; $modif = defined $modif ? '@'.$modif : ''; (my $normcs = $cs) =~ s/[^a-z0-9]//g; if(length $normcs) { $normcs = "iso$normcs" if $normcs !~ /[^0-9-]/; $normcs = '.'.$normcs; } my $fn; for my $f ("/lc_messages/$domain", "/$domain") { $fn ||= _find($index, "$lang$terr$cs$modif$f") || _find($index, "$lang$terr$normcs$modif$f") || _find($index, "$lang$terr$modif$f") || _find($index, "$lang$modif$f") || _find($index, "$lang$f"); } $fn || _find($index, "$domain/$lang$terr$cs$modif") || _find($index, "$domain/$lang$terr$normcs$modif") || _find($index, "$domain/$lang$terr$modif") || _find($index, "$domain/$lang$cs$modif") || _find($index, "$domain/$lang$normcs$modif") || _find($index, "$domain/$lang$modif") || _find($index, "$domain/$lang"); } sub list($;$) { my $self = shift; my $domain = lc shift; my $filter = shift; my $index = $self->index; my @list = map $index->{$_}, grep m!\b\Q$domain\E\b!, keys %$index; defined $filter or return @list; $filter = qr/\.\Q$filter\E$/i if defined $filter && ref $filter ne 'Regexp'; grep $_ =~ $filter, @list; } #------------------------------------- 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/PO.pm0000644000175000001440000002236315000465534022573 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::PO;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; # steal from cheaper module, we have no ::Util for this (yet) use Log::Report::Lexicon::POTcompact (); *_escape = \&Log::Report::Lexicon::POTcompact::_escape; *_unescape = \&Log::Report::Lexicon::POTcompact::_unescape; sub new(@) { my $class = shift; (bless {}, $class)->init( {@_} ); } sub init($) { my ($self, $args) = @_; defined($self->{msgid} = delete $args->{msgid}) or error "no msgid defined for PO"; $self->{plural} = delete $args->{msgid_plural}; $self->{msgstr} = delete $args->{msgstr}; $self->{msgctxt} = delete $args->{msgctxt}; $self->addComment(delete $args->{comment}); $self->addAutomatic(delete $args->{automatic}); $self->fuzzy(delete $args->{fuzzy}); $self->{refs} = {}; $self->addReferences(delete $args->{references}) if defined $args->{references}; $self; } # only for internal usage sub _fast_new($) { bless $_[1], $_[0] } #-------------------- sub msgid() {shift->{msgid}} sub msgctxt() {shift->{msgctxt}} sub plural(;$) { my $self = shift; @_ or return $self->{plural}; if(my $m = $self->{msgstr}) { # prepare msgstr list for multiple translations. $self->{msgstr} = [ $m ] if defined $m && !ref $m; } $self->{plural} = shift; } sub msgstr($;$) { my $self = shift; my $m = $self->{msgstr}; unless($self->{plural}) { $self->{msgstr} = $_[1] if @_==2; return $m; } my $index = shift || 0; @_ ? $m->[$index] = shift : $m->[$index]; } sub comment(@) { my $self = shift; @_ or return $self->{comment}; $self->{comment} = ''; $self->addComment(@_); } sub addComment(@) { my $self = shift; my $comment = $self->{comment}; foreach my $line (ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_) { defined $line or next; $line =~ s/[\r\n]+/\n/; # cleanup line-endings $comment .= $line; } # be sure there is a \n at the end $comment =~ s/\n?\z/\n/ if defined $comment; $self->{comment} = $comment; } sub automatic(@) { my $self = shift; @_ or return $self->{automatic}; $self->{automatic} = ''; $self->addAutomatic(@_); } sub addAutomatic(@) { my $self = shift; my $auto = $self->{automatic}; foreach my $line (ref $_[0] eq 'ARRAY' ? @{$_[0]} : @_) { defined $line or next; $line =~ s/[\r\n]+/\n/; # cleanup line-endings $auto .= $line; } $auto =~ s/\n?\z/\n/ if defined $auto; # be sure there is a \n at the end $self->{automatic} = $auto; } sub references(@) { my $self = shift; if(@_) { $self->{refs} = {}; $self->addReferences(@_); } keys %{$self->{refs}}; } sub addReferences(@) { my $self = shift; my $refs = $self->{refs} ||= {}; @_ or return $refs; $refs->{$_}++ for @_ > 1 ? @_ # list : ref $_[0] eq 'ARRAY' ? @{$_[0]} # array : split " ",$_[0]; # scalar $refs; } sub removeReferencesTo($) { my $refs = $_[0]->{refs}; my $match = qr/^\Q$_[1]\E\:[0-9]+$/; $_ =~ $match && delete $refs->{$_} for keys %$refs; scalar keys %$refs; } sub keepReferencesTo($) { my $refs = shift->{refs}; my $keep = shift; foreach my $ref (keys %$refs) { (my $fn = $ref) =~ s/\:[0-9]+$//; $keep->{$fn} or delete $refs->{$ref}; } scalar keys %$refs; } sub isActive() { $_[0]->{msgid} eq '' || keys %{$_[0]->{refs}} } sub fuzzy(;$) {my $self = shift; @_ ? $self->{fuzzy} = shift : $self->{fuzzy}} sub format(@) { my $format = shift->{format}; return $format->{ (shift) } if @_==1 && !ref $_[0]; # language my @pairs = @_ > 1 ? @_ : ref $_[0] eq 'ARRAY' ? @{$_[0]} : %{$_[0]}; while(@pairs) { my($k, $v) = (shift @pairs, shift @pairs); $format->{$k} = $v; } $format; } sub addFlags($) { my $self = shift; local $_ = shift; my $where = shift; s/^\s+//; s/\s*$//; foreach my $flag (split /\s*\,\s*/) { if($flag eq 'fuzzy') { $self->fuzzy(1) } elsif($flag =~ m/^no-(.*)-format$/) { $self->format($1, 0) } elsif($flag =~ m/^(.*)-format$/) { $self->format($1, 1) } else { warning __x"unknown flag {flag} ignored", flag => $flag; } } $_; } sub fromText($$) { my $class = shift; my @lines = split /[\r\n]+/, shift; my $where = shift || ' unknown location'; my $self = bless {}, $class; # translations which are not used anymore are escaped with #~ # however, we just say: no references found. s/^\#\~\s+// for @lines; my $last; # used for line continuations foreach (@lines) { s/\r?\n$//; if( s/^\#(.)\s?// ) { if($1 =~ /\s/) { $self->addComment($_) } elsif($1 eq '.' ) { $self->addAutomatic($_) } elsif($1 eq ':' ) { $self->addReferences($_) } elsif($1 eq ',' ) { $self->addFlags($_) } else { warning __x"unknown comment type '{cmd}' at {where}" , cmd => "#$1", where => $where; } undef $last; } elsif( s/^\s*(\w+)\s+// ) { my $cmd = $1; my $string = _unescape($_,$where); if($cmd eq 'msgid') { $self->{msgid} = $string; $last = \($self->{msgid}); } elsif($cmd eq 'msgid_plural') { $self->{plural} = $string; $last = \($self->{plural}); } elsif($cmd eq 'msgstr') { $self->{msgstr} = $string; $last = \($self->{msgstr}); } elsif($cmd eq 'msgctxt') { $self->{msgctxt} = $string; $last = \($self->{msgctxt}); } else { warning __x"do not understand command '{cmd}' at {where}" , cmd => $cmd, where => $where; undef $last; } } elsif( s/^\s*msgstr\[(\d+)\]\s*// ) { my $nr = $1; $self->{msgstr}[$nr] = _unescape($_,$where); } elsif( m/^\s*\"/ ) { if(defined $last) { $$last .= _unescape($_,$where) } else { warning __x"quoted line is not a continuation at {where}" , where => $where; } } else { warning __x"do not understand line at {where}:\n {line}" , where => $where, line => $_; } } defined $self->{msgid} or warning __x"no msgid in block {where}", where => $where; $self; } sub toString(@) { my ($self, %args) = @_; my $nplurals = $args{nr_plurals}; my @record; my $comment = $self->comment; if(defined $comment && length $comment) { $comment =~ s/^/# /gm; push @record, $comment; } my $auto = $self->automatic; if(defined $auto && length $auto) { $auto =~ s/^/#. /gm; push @record, $auto; } my @refs = sort $self->references; my $msgid = $self->{msgid} || ''; my $active = $msgid eq '' || @refs ? '' : '#~ '; while(@refs) { my $line = '#:'; $line .= ' '.shift @refs while @refs && length($line) + length($refs[0]) < 80; push @record, "$line\n"; } my @flags = $self->{fuzzy} ? 'fuzzy' : (); push @flags, ($self->{format}{$_} ? '' : 'no-') . $_ . '-format' for sort keys %{$self->{format}}; push @record, "#, ". join(", ", @flags) . "\n" if @flags; my $msgctxt = $self->{msgctxt}; if(defined $msgctxt && length $msgctxt) { push @record, "${active}msgctxt "._escape($msgctxt, "\n$active")."\n"; } push @record, "${active}msgid "._escape($msgid, "\n$active")."\n"; my $msgstr = $self->{msgstr} || []; my @msgstr = ref $msgstr ? @$msgstr : $msgstr; my $plural = $self->{plural}; if(defined $plural) { push @record , "${active}msgid_plural " . _escape($plural, "\n$active") . "\n"; push @msgstr, '' while defined $nplurals && @msgstr < $nplurals; if(defined $nplurals && @msgstr > $nplurals) { warning __x"too many plurals for '{msgid}'", msgid => $msgid; $#msgstr = $nplurals -1; } $nplurals ||= 2; for(my $nr = 0; $nr < $nplurals; $nr++) { push @record, "${active}msgstr[$nr] " . _escape($msgstr[$nr], "\n$active") . "\n"; } } else { warning __x"no plurals for '{msgid}'", msgid => $msgid if @msgstr > 1; push @record , "${active}msgstr " . _escape($msgstr[0], "\n$active") . "\n"; } join '', @record; } sub useless() { my $self = shift; ! $self->references && ! $self->msgstr(0); } *unused = \&useless; # before <1.02 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/Index.pod0000644000175000001440000001510215000465535023464 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::Index - search through available translation files =head1 SYNOPSIS my $index = Log::Report::Lexicon::Index->new($directory); my $fn = $index->find('my-domain', 'nl_NL.utf-8'); =head1 DESCRIPTION This module handles the lookup of translation files for a whole directory tree. It is lazy loading, which means that it will only build the search tree when addressed, not when the object is created. =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Lexicon::Index-EB($directory, %options) Create an index for a certain directory. If the directory does not exist or is empty, then the object will still be created. All files the $directory tree which are recognized as an translation table format which is understood will be listed. Momentarily, those are: =over =item . files with extension "po", see L =item . [0.993] files with extension "mo", see L =back [0.99] Files which are in directories which start with a dot (hidden directories) and files which start with a dot (hidden files) are skipped. =back =head2 Accessors =over 4 =item $obj-EB() Returns the directory name. =back =head2 Search =over 4 =item $obj-EB( $basename, [$absolute] ) Add a certain file to the index. This method returns the $absolute path to that file, which must be used to access it. When not explicitly specified, the $absolute path will be calculated. =item $obj-EB($textdomain, $locale) Lookup the best translation table, according to the rules described in chapter L, below. Returned is a filename, or C if nothing is defined for the $locale (there is no default on this level). =item $obj-EB() For internal use only. Force the creation of the index (if not already done). Returns a hash with key-value pairs, where the key is the lower-cased version of the filename, and the value the case-sensitive version of the filename. =item $obj-EB( $domain, [$extension] ) Returned is a list of filenames which is used to update the list of MSGIDs when source files have changed. All translation files which belong to a certain $domain are listed. The $extension filter can be used to reduce the filenames further, for instance to select only C, C or C files, and ignore readme's. Use an string, without dot and interpreted case-insensitive, or a regular expression. example: my @l = $index->list('my-domain'); my @l = $index->list('my-domain', 'po'); my @l = $index->list('my-domain', qr/^readme/i); =back =head1 DETAILS It's always complicated to find the lexicon files, because the perl package can be installed on any weird operating system. Therefore, you may need to specify the lexicon directory or alternative directories explicitly. However, you may also choose to install the lexicon files in between the perl modules. =head2 merge lexicon files with perl modules By default, the filename which contains the package which contains the textdomain's translator configuration is taken (that can be only one) and changed into a directory name. The path is then extended with C to form the root of the lexicon: the top of the index. After this, the locale indication, the lc-category (usually LC_MESSAGES), and the C followed by C<.po> are added. This is exactly as C does, but then using the PO text file instead of the MO binary file. B<. Example: lexicon in module tree> My module is named C and installed in some of perl's directories, say C<~perl5.8.8>. The module is defining textdomain C. The translation is made into C (locale for Dutch spoken in The Netherlands, utf-8 encoded text file). The default location for the translation table is under ~perl5.8.8/Some/Module/messages/ for instance ~perl5.8.8/Some/Module/messages/nl-NL.utf-8/LC_MESSAGES/my-domain.po There are alternatives, as described in L, for instance ~perl5.8.8/Some/Module/messages/my-domain/nl-NL.utf-8.po ~perl5.8.8/Some/Module/messages/my-domain/nl.po =head2 Locale search The exact gettext defined format of the locale is language[_territory[.codeset]][@modifier] The modifier will be used in above directory search, but only if provided explicitly. The manual C determines the rules. During the search, components of the locale get stripped, in the following order: =over 4 =item 1. codeset =item 2. normalized codeset =item 3. territory =item 4. modifier =back The normalized codeset (character-set name) is derived by =over 4 =item 1. Remove all characters beside numbers and letters. =item 2. Fold letters to lowercase. =item 3. If the same only contains digits prepend the string "iso". =back To speed-up the search for the right table, the full directory tree will be indexed only once when needed the first time. The content of all defined lexicon directories will get merged into one tree. =head2 Example My module is named C and installed in some of perl's directories, say C<~perl5>. The module is defining textdomain C. The translation is made into C (locale for Dutch spoken in The Netherlands, utf-8 encoded text file). The translation table is taken from the first existing of these files: nl-NL.utf-8/LC_MESSAGES/my-domain.po nl-NL.utf-8/LC_MESSAGES/my-domain.po nl-NL.utf8/LC_MESSAGES/my-domain.po nl-NL/LC_MESSAGES/my-domain.po nl/LC_MESSAGES/my-domain.po Then, attempts are made which are not compatible with gettext. The advantage is that the directory structure is much simpler. The idea is that each domain has its own locale installation directory, instead of everything merged in one place, what gettext presumes. In order of attempts: nl-NL.utf-8/my-domain.po nl-NL.utf8/my-domain.po nl-NL/my-domain.po nl/my-domain.po my-domain/nl-NL.utf8.po my-domain/nl-NL.po my-domain/nl.po Filenames may get mutulated by the platform (which we will try to hide from you [please help improve this]), and are treated case-INsensitive! =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/MOTcompact.pod0000644000175000001440000001013015000465535024417 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::MOTcompact - use translations from an MO file =head1 INHERITANCE Log::Report::Lexicon::MOTcompact is a Log::Report::Lexicon::Table =head1 SYNOPSIS # using a MO table efficiently my $mot = Log::Report::Lexicon::MOTcompact->read('mo/nl.mo') or die; my $header = $mot->msgid(''); print $mot->msgstr($msgid, 3); =head1 DESCRIPTION This module is translating, based on MO files (binary versions of the PO files, the "Machine Object" format) Actually, this module is not "compact" anymore: not trading off speed for memory. That may change again in the future. To get a MO file, you first need a PO file. Then run F, which is part of the gnu gettext package. msgfmt -cv -o $domain.mo $domain.po # -c = --check-format & --check-header & --check-domain # -v = --verbose # -o = --output-file Extends L<"DESCRIPTION" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Constructors">. =over 4 =item Log::Report::Lexicon::MOTcompact-EB(%options) Inherited, see L =item Log::Report::Lexicon::MOTcompact-EB($filename, %options) Read the MOT table information from $filename. -Option --Default charset =over 2 =item charset => STRING The character-set which is used for the file. When not specified, it is taken from the "Content-Type" field in the PO-file. =back =back =head2 Attributes Extends L<"Attributes" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Attributes">. =over 4 =item $obj-EB() Returns the name of the source file for this data. =item $obj-EB() Returns a HASH of all defined PO objects, organized by msgid. Please try to avoid using this: use L for lookup. =item $obj-EB() Returns the character-set as found in the PO-file. The strings are converted into utf8 before you use them in the program. =back =head2 Managing PO's Extends L<"Managing PO's" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Managing PO's">. =head3 Translation Extends L<"Translation" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Translation">. =over 4 =item $obj-EB( STRING, [$msgctxt] ) Lookup the translations with the STRING. Returns a SCALAR, when only one translation is known, and an ARRAY when we have plural forms. Returns C when the translation is not defined. =item $obj-EB( $msgid, [$count, $msgctxt] ) Returns the translated string for $msgid. When not specified, $count is 1 (the singular form). =back =head3 Administration Extends L<"Administration" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Administration">. =over 4 =item $obj-EB($po) Inherited, see L =item $obj-EB
($field) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB($count) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB( [$active] ) Inherited, see L =back =head1 DIAGNOSTICS =over 4 =item Error: only acceptable parameter is 'ACTIVE' =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/PO.pod0000644000175000001440000001320615000465535022736 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::PO - one translation definition =head1 SYNOPSIS =head1 DESCRIPTION This module is administering one translation object. Sets of PO records are kept in a POT file, implemented in L. =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Lexicon::PO-EB(%options) -Option --Default automatic "" comment [] format [] fuzzy false msgctxt undef msgid msgid_plural undef msgstr "" or [] references [] =over 2 =item automatic => PARAGRAPH Automatically added comments. See L. =item comment => PARAGRAPH Translator added comments. See L. =item format => ARRAY|HASH See L. Either an ARRAY with PAIRS or a HASH with that same information. =item fuzzy => BOOLEAN The string is not yet translated, some smart guesses may have been made. See L. =item msgctxt => STRING Context string: text around the msgid itself. =item msgid => STRING =item msgid_plural => STRING =item msgstr => STRING|ARRAY-OF-STRING The translations for the msgid. When msgid_plural is defined, then an ARRAY must be provided. =item references => STRING|ARRAY-OF-LOCATIONS The STRING is a blank separated list of LOCATIONS. LOCATIONs are of the form C, for instance C See L =back =back =head2 Attributes =over 4 =item $obj-EB(LIST|ARRAY|STRING) Add multiple lines to the translator's comment block. Returns an empty string if there are no comments. =item $obj-EB(LIST|ARRAY|STRING) Add multiple lines to the translator's comment block. Returns an empty string if there are no comments. =item $obj-EB(STRING) Parse a "flags" line. =item $obj-EB(STRING|LIST|ARRAY) The STRING is a blank separated list of LOCATIONS. The LIST and ARRAY contain separate LOCATIONs. A LOCATION is of the form C. Returns the internal HASH with references. =item $obj-EB( [LIST|ARRAY|STRING] ) Returns a STRING which contains the cleaned paragraph of automatically added comments. If an argument is specified, it will replace the current comment. =item $obj-EB( [LIST|ARRAY|STRING] ) Returns a STRING which contains the cleaned paragraph of translator's comment. If an argument is specified, it will replace the current comment. =item $obj-EB( $language | ) When one $language is specified, it looks whether a C<$language-format> or C is present in the line of FLAGS. This will return C<1> (true) in the first case, C<0> (false) in the second case. It will return C (also false) in case that both are not present. You can also specify PAIRS: the key is a language name, and the value is either C<0>, C<1>, or C. example: use of format() # getters if($po->format('c')) ... unless($po->format('perl-brace')) ... if(defined $po->format('java')) ... # setters $po->format(java => 1); # results in 'java-format' $po->format(java => 0); # results in 'no-java-format' $po->format(java => undef); # results in '' =item $obj-EB( [BOOLEAN] ) Returns whether the translation needs human inspection. =item $obj-EB() Returns whether the translation has any references, or is the header. =item $obj-EB($table) Remove all references which are not found as key in the hash $table. Returns the number of references left. =item $obj-EB() Returns the message context, if provided. =item $obj-EB() Returns the actual msgid, which cannot be C. =item $obj-EB( [$index, [STRING]] ) With a STRING, a new translation will be set. Without STRING, a lookup will take place. When no plural is defined, the $index is ignored. =item $obj-EB( [STRING] ) Returns the actual msgid_plural, which can be C. =item $obj-EB( [STRING|LIST|ARRAY] ) Returns an unsorted list of LOCATIONS. When options are specified, then those will be used to replace all currently defined references. Returns the unsorted LIST of references. =item $obj-EB($filename) Remove all the references to the indicate $filename from the list. Returns the number of refs left. =back =head2 Parsing =over 4 =item Log::Report::Lexicon::PO-EB( STRING, [$where] ) Parse the STRING into a new PO object. The $where string should explain the location of the STRING, to be used in error messages. =item $obj-EB(%options) Format the object into a multi-lined string. -Option --Default nr_plurals undef =over 2 =item nr_plurals => INTEGER If the number of plurals is specified, then the plural translation list can be checked for the correct length. Otherwise, no smart behavior is attempted. =back =item $obj-EB() [1.02] The message-id has no references anymore B no translations. =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/Table.pod0000644000175000001440000000641515000465535023453 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::Table - generic interface to translation tables =head1 INHERITANCE Log::Report::Lexicon::Table is extended by Log::Report::Lexicon::MOTcompact Log::Report::Lexicon::POT Log::Report::Lexicon::POTcompact =head1 SYNOPSIS # use one of the extensions, for instance: my $pot = Log::Report::Lexicon::POT ->read('po/nl.po', charset => 'utf-8') or panic; =head1 DESCRIPTION This base class defines the generic interface for translation tables. Currently, there are three extensions: =over 4 =item * L This is a relatively heavy implementation, used to read but also to write PO files. It is used by F, for instance, to administer the collection of discovered msgid's. =item * L Light-weighted read-only access to PO-file information. =item * L Read-only access to MO-file information. These binary MO-files are super efficient. =back =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Lexicon::Table-EB(%options) =back =head2 Attributes =head2 Managing PO's =head3 Translation =over 4 =item $obj-EB( STRING, [$msgctxt] ) Lookup the L with the STRING. Returns C when not defined. =item $obj-EB( $msgid, [$count, $msgctxt] ) Returns the translated string for $msgid. When not specified, $count is 1. =back =head3 Administration =over 4 =item $obj-EB($po) Add the information from a $po into this POT. If the msgid of the $po is already known, that is an error. =item $obj-EB
($field) The translation of a blank MSGID is used to store a MIME header, which contains some meta-data. The $field value is looked-up (case-insensitive) and returned. =item $obj-EB() Returns the number of plurals, when not known then '2'. =item $obj-EB($count) Returns the msgstr index used to translate a value of $count. =item $obj-EB() This method needs to be called after setting (reading or creating) a new table header, to interpret the plural algorithm as specified in the C header field. [1.09] The header field is not required when not used. A full list of plural forms per language can be found at F =item $obj-EB( [$active] ) Returns a list with all defined L objects. When the string C<$active> is given as parameter, only objects which have references are returned. =back =head1 DIAGNOSTICS =over 4 =item Error: only acceptable parameter is 'ACTIVE' =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/POT.pm0000644000175000001440000001745615000465534022726 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::POT;{ our $VERSION = '1.12'; } use base 'Log::Report::Lexicon::Table'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use Log::Report::Lexicon::PO (); use POSIX qw/strftime/; use List::Util qw/sum/; use Scalar::Util qw/blessed/; use Encode qw/decode/; use constant MSGID_HEADER => ''; sub init($) { my ($self, $args) = @_; $self->{LRLP_fn} = $args->{filename}; $self->{LRLP_index} = $args->{index} || {}; $self->{LRLP_charset} = $args->{charset} || 'UTF-8'; my $version = $args->{version}; my $domain = $args->{textdomain} or error __"textdomain parameter is required"; my $forms = $args->{plural_forms}; unless($forms) { my $nrplurals = $args->{nr_plurals} || 2; my $algo = $args->{plural_alg} || 'n!=1'; $forms = "nplurals=$nrplurals; plural=($algo);"; } $self->_createHeader ( project => $domain . (defined $version ? " $version" : '') , forms => $forms , charset => $args->{charset} , date => $args->{date} ); $self->setupPluralAlgorithm; $self; } sub read($@) { my ($class, $fn, %args) = @_; my $self = bless {LRLP_index => {}}, $class; my $charset = $args{charset}; $charset = $1 if !$charset && $fn =~ m!\.([\w-]+)(?:\@[^/\\]+)?\.po$!i; my $fh; if(defined $charset) { open $fh, "<:encoding($charset):crlf", $fn or fault __x"cannot read in {cs} from file {fn}" , cs => $charset, fn => $fn; } else { open $fh, '<:raw:crlf', $fn or fault __x"cannot read from file {fn} (unknown charset)", fn=>$fn; } local $/ = "\n\n"; my $linenr = 1; # $/ frustrates $fh->input_line_number while(1) { my $location = "$fn line $linenr"; my $block = <$fh>; defined $block or last; $linenr += $block =~ tr/\n//; $block =~ s/\s+\z//s; length $block or last; unless($charset) { $charset = $block =~ m/\"content-type:.*?charset=["']?([\w-]+)/mi ? $1 : error __x"cannot detect charset in {fn}", fn => $fn; trace "auto-detected charset $charset for $fn"; binmode $fh, ":encoding($charset):crlf"; $block = decode $charset, $block or error __x"unsupported charset {charset} in {fn}" , charset => $charset, fn => $fn; } my $po = Log::Report::Lexicon::PO->fromText($block, $location); $self->add($po) if $po; } close $fh or fault __x"failed reading from file {fn}", fn => $fn; $self->{LRLP_fn} = $fn; $self->{LRLP_charset} = $charset; $self->setupPluralAlgorithm; $self; } sub write($@) { my $self = shift; my $file = @_%2 ? shift : $self->filename; my %args = @_; defined $file or error __"no filename or file-handle specified for PO"; my $need_refs = $args{only_active}; my @opt = (nr_plurals => $self->nrPlurals); my $fh; if(ref $file) { $fh = $file } else { my $layers = '>:encoding('.$self->charset.')'; open $fh, $layers, $file or fault __x"cannot write to file {fn} with {layers}" , fn => $file, layers => $layers; } $fh->print($self->msgid(MSGID_HEADER)->toString(@opt)); my $index = $self->index; foreach my $msgid (sort keys %$index) { next if $msgid eq MSGID_HEADER; my $rec = $index->{$msgid}; my @recs = blessed $rec ? $rec # one record with $msgid : @{$rec}{sort keys %$rec}; # multiple records, msgctxt foreach my $po (@recs) { next if $po->useless; next if $need_refs && !$po->references; $fh->print("\n", $po->toString(@opt)); } } $fh->close or failure __x"write errors for file {fn}", fn => $file; $self; } #----------------------- sub charset() {shift->{LRLP_charset}} sub index() {shift->{LRLP_index}} sub filename() {shift->{LRLP_fn}} sub language() { shift->filename =~ m![/\\](\w+)[^/\\]*$! ? $1 : undef } #----------------------- sub msgid($;$) { my ($self, $msgid, $msgctxt) = @_; my $msgs = $self->index->{$msgid} or return; return $msgs if blessed $msgs && (!$msgctxt || $msgctxt eq $msgs->msgctxt); $msgs->{$msgctxt}; } sub msgstr($;$$) { my ($self, $msgid, $count, $msgctxt) = @_; my $po = $self->msgid($msgid, $msgctxt) or return undef; $count //= 1; $po->msgstr($self->pluralIndex($count)); } sub add($) { my ($self, $po) = @_; my $msgid = $po->msgid; my $index = $self->index; my $h = $index->{$msgid}; $h or return $index->{$msgid} = $po; $h = $index->{$msgid} = +{ ($h->msgctxt // '') => $h } if blessed $h; my $ctxt = $po->msgctxt // ''; error __x"translation already exists for '{msgid}' with '{ctxt}" , msgid => $msgid, ctxt => $ctxt if $h->{$ctxt}; $h->{$ctxt} = $po; } sub translations(;$) { my $self = shift; @_ or return map +(blessed $_ ? $_ : values %$_) , values %{$self->index}; error __x"the only acceptable parameter is 'ACTIVE', not '{p}'", p => $_[0] if $_[0] ne 'ACTIVE'; grep $_->isActive, $self->translations; } sub _now() { strftime "%Y-%m-%d %H:%M%z", localtime } sub header($;$) { my ($self, $field) = (shift, shift); my $header = $self->msgid(MSGID_HEADER) or error __x"no header defined in POT for file {fn}" , fn => $self->filename; if(!@_) { my $text = $header->msgstr(0) || ''; return $text =~ m/^\Q$field\E\:\s*([^\n]*?)\;?\s*$/im ? $1 : undef; } my $content = shift; my $text = $header->msgstr(0); for($text) { if(defined $content) { s/^\Q$field\E\:([^\n]*)/$field: $content/im # change || s/\z/$field: $content\n/; # new } else { s/^\Q$field\E\:[^\n]*\n?//im; # remove } } $header->msgstr(0, $text); $content; } sub updated(;$) { my $self = shift; my $date = shift || _now; $self->header('PO-Revision-Date', $date); $date; } ### internal sub _createHeader(%) { my ($self, %args) = @_; my $date = $args{date} || _now; my $header = Log::Report::Lexicon::PO->new ( msgid => MSGID_HEADER, msgstr => <<__CONFIG); Project-Id-Version: $args{project} Report-Msgid-Bugs-To: POT-Creation-Date: $date PO-Revision-Date: $date Last-Translator: Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=$args{charset} Content-Transfer-Encoding: 8bit Plural-Forms: $args{forms} __CONFIG my $version = $Log::Report::VERSION || '0.0'; $header->addAutomatic("Header generated with ".__PACKAGE__." $version\n"); $self->index->{&MSGID_HEADER} = $header if $header; $header; } sub removeReferencesTo($) { my ($self, $filename) = @_; sum map $_->removeReferencesTo($filename), $self->translations; } sub keepReferencesTo($) { my ($self, $keep) = @_; sum map $_->keepReferencesTo($keep), $self->translations; } sub stats() { my $self = shift; my %stats = (msgids => 0, fuzzy => 0, inactive => 0); foreach my $po ($self->translations) { next if $po->msgid eq MSGID_HEADER; $stats{msgids}++; $stats{fuzzy}++ if $po->fuzzy; $stats{inactive}++ if !$po->isActive && !$po->useless; } \%stats; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/MOTcompact.pm0000644000175000001440000001451115000465534024257 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::MOTcompact;{ our $VERSION = '1.12'; } use base 'Log::Report::Lexicon::Table'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use Fcntl qw(SEEK_SET); use Encode qw(find_encoding); use constant MAGIC_NUMBER => 0x95_04_12_DE; sub read($@) { my ($class, $fn, %args) = @_; my $charset = $args{charset}; $charset = $1 if !$charset && $fn =~ m!\.([\w-]+)(?:\@[^/\\]+)?\.g?mo$!i; my $enc; if(defined $charset) { $enc = find_encoding($charset) or error __x"unsupported explicit charset {charset} for {fn}" , charset => $charset, fn => $fn; } my (%index, %locs); my %self = +( index => \%index # fully prepared ::PO objects , locs => \%locs # know where to find it , filename => $fn ); my $self = bless \%self, $class; my $fh; open $fh, "<:raw", $fn or fault __x"cannot read mo from file {fn}", fn => $fn; # The magic number will tell us the byte-order # See http://www.gnu.org/software/gettext/manual/html_node/MO-Files.html # Found in a bug-report that msgctxt are prepended to the msgid with # a separating EOT (4) my ($magic, $superblock, $originals, $translations); CORE::read $fh, $magic, 4 or fault __x"cannot read magic from {fn}", fn => $fn; my $byteorder = $magic eq pack('V', MAGIC_NUMBER) ? 'V' : $magic eq pack('N', MAGIC_NUMBER) ? 'N' : error __x"unsupported file type (magic number is {magic%x})" , magic => $magic; # The superblock contains pointers to strings CORE::read $fh, $superblock, 6*4 # 6 times a 32 bit int or fault __x"cannot read superblock from {fn}", fn => $fn; my ( $format_rev, $nr_strings, $offset_orig, $offset_trans , $size_hash, $offset_hash ) = unpack $byteorder x 6, $superblock; # warn "($format_rev, $nr_strings, $offset_orig, $offset_trans # , $size_hash, $offset_hash)"; # Read location of all originals seek $fh, $offset_orig, SEEK_SET or fault __x"cannot seek to {loc} in {fn} for originals" , loc => $offset_orig, fn => $fn; CORE::read $fh, $originals, $nr_strings*8 # each string 2*4 bytes or fault __x"cannot read originals from {fn}, need {size} at {loc}" , fn => $fn, loc => $offset_orig, size => $nr_strings*4; my @origs = unpack $byteorder.'*', $originals; # Read location of all translations seek $fh, $offset_trans, SEEK_SET or fault __x"cannot seek to {loc} in {fn} for translations" , loc => $offset_orig, fn => $fn; CORE::read $fh, $translations, $nr_strings*8 # each string 2*4 bytes or fault __x"cannot read translations from {fn}, need {size} at {loc}" , fn => $fn, loc => $offset_trans, size => $nr_strings*4; my @trans = unpack $byteorder.'*', $translations; # We need the originals as index to the translations (unless there # is a HASH build-in... which is not defined) # The strings are strictly ordered, the spec tells me, to allow binary # search. Better swiftly process the whole block into a hash. my ($orig_start, $orig_end) = ($origs[1], $origs[-1]+$origs[-2]); seek $fh, $orig_start, SEEK_SET or fault __x"cannot seek to {loc} in {fn} for msgid strings" , loc => $orig_start, fn => $fn; my ($orig_block, $trans_block); my $orig_block_size = $orig_end - $orig_start; CORE::read $fh, $orig_block, $orig_block_size or fault __x"cannot read msgids from {fn}, need {size} at {loc}" , fn => $fn, loc => $orig_start, size => $orig_block_size; my ($trans_start, $trans_end) = ($trans[1], $trans[-1]+$trans[-2]); seek $fh, $trans_start, SEEK_SET or fault __x"cannot seek to {loc} in {fn} for transl strings" , loc => $trans_start, fn => $fn; my $trans_block_size = $trans_end - $trans_start; CORE::read $fh, $trans_block, $trans_block_size or fault __x"cannot read translations from {fn}, need {size} at {loc}" , fn => $fn, loc => $trans_start, size => $trans_block_size; while(@origs) { my ($id_len, $id_loc) = (shift @origs, shift @origs); my $msgid_b = substr $orig_block, $id_loc-$orig_start, $id_len; my $msgctxt_b = $msgid_b =~ s/(.*)\x04// ? $1 : ''; my ($trans_len, $trans_loc) = (shift @trans, shift @trans); my $msgstr_b = substr $trans_block, $trans_loc - $trans_start, $trans_len; unless(defined $charset) { $msgid_b eq '' or error __x"the header is not the first entry, needed for charset in {fn}", fn => $fn; $charset = $msgstr_b =~ m/^content-type:.*?charset=["']?([\w-]+)/mi ? $1 : error __x"cannot detect charset in {fn}", fn => $fn; trace "auto-detected charset $charset for $fn"; $enc = find_encoding($charset) or error __x"unsupported charset {charset} in {fn}" , charset => $charset, fn => $fn; } my $msgid = $enc->decode($msgid_b); my $msgctxt = $enc->decode($msgctxt_b); my @msgstr = map $enc->decode($_), split /\0x00/, $msgstr_b; $index{"$msgid#$msgctxt"} = @msgstr > 1 ? \@msgstr : $msgstr[0]; } close $fh or failure __x"failed reading from file {fn}", fn => $fn; $self->{origcharset} = $charset; $self->setupPluralAlgorithm; $self; } #--------- sub index() {shift->{index}} sub filename() {shift->{filename}} sub originalCharset() {shift->{origcharset}} #--------------- sub msgid($;$) { my ($self, $msgid, $msgctxt) = @_; my $tag = $msgid.'#'.($msgctxt//''); $self->{index}{$tag}; } sub msgstr($;$$) { my $po = $_[0]->msgid($_[1], $_[3]) or return undef; ref $po # no plurals defined or return $po; # speed!!! $po->[$_[0]->{algo}->(defined $_[2] ? $_[2] : 1)] || $po->[$_[0]->{algo}->(1)]; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/POTcompact.pm0000644000175000001440000001050715000465534024263 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::POTcompact;{ our $VERSION = '1.12'; } use base 'Log::Report::Lexicon::Table'; use warnings; use strict; use Log::Report 'log-report-lexicon'; use Log::Report::Util qw/escape_chars unescape_chars/; use Encode qw/find_encoding/; sub _unescape($$); sub _escape($$); sub read($@) { my ($class, $fn, %args) = @_; my $self = bless {}, $class; my $charset = $args{charset}; # Try to pick-up charset from the filename (which may contain a modifier) $charset = $1 if !$charset && $fn =~ m!\.([\w-]+)(?:\@[^/\\]+)?\.po$!i; my $fh; if($charset) { open $fh, "<:encoding($charset):crlf", $fn or fault __x"cannot read in {charset} from file {fn}" , charset => $charset, fn => $fn; } else { open $fh, '<:raw:crlf', $fn or fault __x"cannot read from file {fn} (unknown charset)", fn=>$fn; } # Speed! my $msgctxt = ''; my ($last, $msgid, @msgstr); my $index = $self->{index} ||= {}; my $add = sub { unless($charset) { $msgid eq '' or error __x"header not found for charset in {fn}", fn => $fn; $charset = $msgstr[0] =~ m/^content-type:.*?charset=["']?([\w-]+)/mi ? $1 : error __x"cannot detect charset in {fn}", fn => $fn; my $enc = find_encoding($charset) or error __x"unsupported charset {charset} in {fn}" , charset => $charset, fn => $fn; trace "auto-detected charset $charset for $fn"; binmode $fh, ":encoding($charset):crlf"; $_ = $enc->decode($_) for @msgstr, $msgctxt; } $index->{"$msgid#$msgctxt"} = @msgstr > 1 ? [@msgstr] : $msgstr[0]; ($msgctxt, $msgid, @msgstr) = (''); }; LINE: while(my $line = $fh->getline) { next if substr($line, 0, 1) eq '#'; if($line =~ m/^\s*$/) # blank line starts new { $add->() if @msgstr; next LINE; } if($line =~ s/^msgctxt\s+//) { $msgctxt = _unescape $line, $fn; $last = \$msgctxt; } elsif($line =~ s/^msgid\s+//) { $msgid = _unescape $line, $fn; $last = \$msgid; } elsif($line =~ s/^msgstr\[(\d+)\]\s*//) { $last = \($msgstr[$1] = _unescape $line, $fn); } elsif($line =~ s/^msgstr\s+//) { $msgstr[0] = _unescape $line, $fn; $last = \$msgstr[0]; } elsif($last && $line =~ m/^\s*\"/) { $$last .= _unescape $line, $fn; } } $add->() if @msgstr; # don't forget the last close $fh or failure __x"failed reading from file {fn}", fn => $fn; $self->{origcharset} = $charset; $self->{filename} = $fn; $self->setupPluralAlgorithm; $self; } #------------------ sub filename() {shift->{filename}} sub originalCharset() {shift->{origcharset}} #------------------ sub index() {shift->{index}} # The index is a HASH with "$msg#$msgctxt" keys. If there is no # $msgctxt, then there still is the # sub msgid($) { $_[0]->{index}{$_[1].'#'.($_[2]//'')} } # speed!!! sub msgstr($;$$) { my ($self, $msgid, $count, $ctxt) = @_; $ctxt //= ''; my $po = $self->{index}{"$msgid#$ctxt"} or return undef; ref $po # no plurals defined or return $po; $po->[$self->{algo}->($count // 1)] || $po->[$self->{algo}->(1)]; } # ### internal helper routines, shared with ::PO.pm and ::POT.pm # sub _unescape($$) { unless( $_[0] =~ m/^\s*\"(.*)\"\s*$/ ) { warning __x"string '{text}' not between quotes at {location}" , text => $_[0], location => $_[1]; return $_[0]; } unescape_chars $1; } sub _escape($$) { my @escaped = map { '"' . escape_chars($_) . '"' } defined $_[0] && length $_[0] ? split(/(?<=\n)/, $_[0]) : ''; unshift @escaped, '""' if @escaped > 1; join $_[1], @escaped; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/Table.pm0000644000175000001440000000337015000465534023301 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon::Table;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; use POSIX qw/strftime/; use IO::File (); use List::Util qw/sum/; sub new(@) { my $class = shift; (bless {}, $class)->init({@_}) } sub init($) {shift} #----------------------- #----------------------- sub msgid($;$) {panic "not implemented"} sub msgstr($;$$) {panic "not implemented"} #------------------ sub add($) {panic "not implemented"} sub translations(;$) {panic "not implemented"} sub pluralIndex($) { my ($self, $count) = @_; my $algo = $self->{algo} or error __x"there is no Plural-Forms field in the header, but needed"; $algo->($count); } sub setupPluralAlgorithm() { my $self = shift; my $forms = $self->header('Plural-Forms') or return; my $alg = $forms =~ m/plural\=([n%!=><\s\d|&?:()]+)/ ? $1 : "n!=1"; $alg =~ s/\bn\b/(\$_[0])/g; my $code = eval "sub(\$) {$alg}"; $@ and error __x"invalid plural-form algorithm '{alg}'", alg => $alg; $self->{algo} = $code; $self->{nplurals} = $forms =~ m/\bnplurals\=(\d+)/ ? $1 : 2; $self; } sub nrPlurals() {shift->{nplurals}} sub header($@) { my ($self, $field) = @_; my $header = $self->msgid('') or return; $header =~ m/^\Q$field\E\:\s*([^\n]*?)\;?\s*$/im ? $1 : undef; } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon/POTcompact.pod0000644000175000001440000000773415000465535024442 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon::POTcompact - use translations from a POT file =head1 INHERITANCE Log::Report::Lexicon::POTcompact is a Log::Report::Lexicon::Table =head1 SYNOPSIS # using a PO table efficiently my $pot = Log::Report::Lexicon::POTcompact->read('po/nl.po') or die; my $header = $pot->msgid(''); print $pot->msgstr('msgid', 3); =head1 DESCRIPTION This module is translating, based on PO files. PO files are used to store translations in humanly readable format for most of existing translation frameworks, like GNU gettext and Perl's Maketext. Internally, this module tries to be as efficient as possible: high speed and low memory foot-print. You will not be able to sub-class this class cleanly. If you like to change the content of PO files, then use L. Extends L<"DESCRIPTION" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"DESCRIPTION">. =head1 METHODS Extends L<"METHODS" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"METHODS">. =head2 Constructors Extends L<"Constructors" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Constructors">. =over 4 =item Log::Report::Lexicon::POTcompact-EB(%options) Inherited, see L =item Log::Report::Lexicon::POTcompact-EB($filename, %options) Read the POT table information from $filename, as compact as possible. Comments, plural-form, and such are lost on purpose: they are not needed for translations. -Option --Default charset undef =over 2 =item charset => STRING When the charset is not specified, it will be taken from the content-type field in the po-file header. =back =back =head2 Attributes Extends L<"Attributes" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Attributes">. =over 4 =item $obj-EB() Returns the name of the source file for this data. =item $obj-EB() [1.09] Returns the character-set of the strings found in the file. They will get translated into utf8 before being used in Perl. =back =head2 Managing PO's Extends L<"Managing PO's" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Managing PO's">. =head3 Translation Extends L<"Translation" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Translation">. =over 4 =item $obj-EB( STRING, [$msgctxt] ) Lookup the translations with the STRING. Returns a SCALAR, when only one translation is known, and an ARRAY wherein there are multiple. Returns C when the translation is not defined. =item $obj-EB( $msgid, [$count, [$msgctxt] ) Returns the translated string for $msgid. When not specified, $count is 1 (the single form). =back =head3 Administration Extends L<"Administration" in Log::Report::Lexicon::Table|Log::Report::Lexicon::Table/"Administration">. =over 4 =item $obj-EB($po) Inherited, see L =item $obj-EB
($field) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB($count) Inherited, see L =item $obj-EB() Inherited, see L =item $obj-EB( [$active] ) Inherited, see L =back =head1 DIAGNOSTICS =over 4 =item Error: only acceptable parameter is 'ACTIVE' =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Extract.pm0000644000175000001440000001402615000465534022263 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Extract;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; use Log::Report::Lexicon::Index (); use Log::Report::Lexicon::POT (); sub new(@) { my $class = shift; (bless {}, $class)->init( {@_} ); } sub init($) { my ($self, $args) = @_; my $lexi = $args->{lexicon} || $args->{lexicons} or error __"extractions require an explicit lexicon directory"; -d $lexi or mkdir $lexi or fault __x"cannot create lexicon directory {dir}", dir => $lexi; $self->{LRE_index} = Log::Report::Lexicon::Index->new($lexi); $self->{LRE_charset} = $args->{LRE_charset} || 'utf-8'; $self->{LRE_domains} = {}; $self; } #--------------- sub index() {shift->{LRE_index}} sub charset() {shift->{LRE_charset}} sub domains() {sort keys %{shift->{LRE_domains}}} sub pots($) { my ($self, $domain) = @_; my $r = $self->{LRE_domains}{$domain}; $r ? @$r : (); } sub addPot($$%) { my ($self, $domain, $pot) = @_; push @{$self->{LRE_domains}{$domain}}, ref $pot eq 'ARRAY' ? @$pot : $pot if $pot; } #--------------- sub process($@) { my ($self, $fn, %opts) = @_; panic "not implemented"; } sub cleanup(%) { my ($self, %args) = @_; my $keep = $args{keep} || {}; $keep = +{ map +($_ => 1), @$keep } if ref $keep eq 'ARRAY'; foreach my $domain ($self->domains) { $_->keepReferencesTo($keep) for $self->pots($domain); } } sub showStats(;$) { my $self = shift; my @domains = @_ ? @_ : $self->domains; dispatcher needs => 'INFO' or return; foreach my $domain (@domains) { my $pots = $self->{LRE_domains}{$domain} or next; my ($msgids, $fuzzy, $inactive) = (0, 0, 0); foreach my $pot (@$pots) { my $stats = $pot->stats; next unless $stats->{fuzzy} || $stats->{inactive}; $msgids = $stats->{msgids}; next if $msgids == $stats->{fuzzy}; # ignore the template notice __x "{domain}: {fuzzy%3d} fuzzy, {inact%3d} inactive in {filename}" , domain => $domain, fuzzy => $stats->{fuzzy} , inact => $stats->{inactive}, filename => $pot->filename; $fuzzy += $stats->{fuzzy}; $inactive += $stats->{inactive}; } if($fuzzy || $inactive) { info __xn "{domain}: one file with {ids} msgids, {f} fuzzy and {i} inactive translations" , "{domain}: {_count} files each {ids} msgids, {f} fuzzy and {i} inactive translations in total" , scalar(@$pots), domain => $domain , f => $fuzzy, ids => $msgids, i => $inactive } else { info __xn "{domain}: one file with {ids} msgids" , "{domain}: {_count} files with each {ids} msgids" , scalar(@$pots), domain => $domain, ids => $msgids; } } } sub write(;$) { my ($self, $domain) = @_; unless(defined $domain) # write all { $self->write($_) for $self->domains; return; } my $pots = delete $self->{LRE_domains}{$domain} or return; # nothing found for my $pot (@$pots) { $pot->updated; $pot->write; } $self; } sub DESTROY() {shift->write} sub _reset($$) { my ($self, $domain, $fn) = @_; my $pots = $self->{LRE_domains}{$domain} ||= $self->_read_pots($domain); $_->removeReferencesTo($fn) for @$pots; } sub _read_pots($) { my ($self, $domain) = @_; my $index = $self->index; my $charset = $self->charset; my @pots = map Log::Report::Lexicon::POT->read($_, charset=> $charset), $index->list($domain); trace __xn "found one pot file for domain {domain}" , "found {_count} pot files for domain {domain}" , @pots, domain => $domain; return \@pots if @pots; # new text-domain found, start template my $fn = $index->addFile("$domain.$charset.po"); info __x"starting new textdomain {domain}, template in {filename}" , domain => $domain, filename => $fn; my $pot = Log::Report::Lexicon::POT->new ( textdomain => $domain , filename => $fn , charset => $charset , version => 0.01 ); [ $pot ]; } sub store($$$$;$) { my ($self, $domain, $fn, $linenr, $msgid, $plural) = @_; my $textdomain = textdomain $domain; my $context = $textdomain->contextRules; foreach my $pot ($self->pots($domain)) { my ($stripped, $msgctxts); if($context) { my $lang = $pot->language || 'en'; ($stripped, $msgctxts) = $context->expand($msgid, $lang); if($plural && $plural =~ m/\{[^}]*\<\w+/) { error __x"no context tags allowed in plural `{msgid}'" , msgid => $plural; } } else { $stripped = $msgid; } $msgctxts && @$msgctxts or $msgctxts = [undef]; MSGCTXT: foreach my $msgctxt (@$msgctxts) { #warn "($stripped, $msgctxt)"; if(my $po = $pot->msgid($stripped, $msgctxt)) { $po->addReferences( ["$fn:$linenr"]); $po->plural($plural) if $plural; next MSGCTXT; } my $format = $stripped =~ m/\{/ ? 'perl-brace' : 'perl'; my $po = Log::Report::Lexicon::PO->new ( msgid => $stripped , msgid_plural => $plural , msgctxt => $msgctxt , fuzzy => 1 , format => $format , references => [ "$fn:$linenr" ] ); $pot->add($po); } } } 1; Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon.pm0000644000175000001440000000126315000465534022251 0ustar00markovusers00000000000000# Copyrights 2007-2025 by [Mark Overmeer ]. # For other contributors see ChangeLog. # See the manual pages for details on the licensing terms. # Pod stripped from pm file by OODoc 2.03. # This code is part of distribution Log-Report-Lexicon. Meta-POD processed # with OODoc into POD and HTML manual-pages. See README.md # Copyright Mark Overmeer. Licensed under the same terms as Perl itself. package Log::Report::Lexicon;{ our $VERSION = '1.12'; } use warnings; use strict; use Log::Report 'log-report-lexicon'; sub new(@) { my $class = shift; (bless {}, $class)->init( {@_} ); } sub init($) { shift } # $self, $args #-------------- #-------------- 1; Log-Report-Lexicon-1.12/lib/Log/Report/Win32Locale.pod0000644000175000001440000000513015000465535023036 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Win32Locale - unix/windows locales =head1 INHERITANCE Log::Report::Win32Locale is an Exporter =head1 SYNOPSIS # Only usable on Windows print codepage_to_iso(0x0413); # nl-NL print iso_to_codepage('nl_NL'); # 1043 printf "%x", iso_to_codepage('nl_NL'); # 413 my $iso = iso_locale(ms_codepage_id()); my $iso = iso_locale; # same print charset_encoding; # cp1252 print ms_codepage_id; # 1043 print ms_install_codepage_id; # 1043 print ms_locale; # Dutch (Netherlands) =head1 DESCRIPTION Windows uses different locales to represent languages: codepages. Programs which are written with Log::Report however, will contain ISO encoded language names; this module translates between them. The algorithms in this module are based on Win32::Locale and Win32::Codepage. =head1 FUNCTIONS =over 4 =item B() Returns the encoding name (usable with module Encode) based on the current codepage. For example, C for iso-8859-1 (latin-1) or C for Shift-JIS Japanese. Returns undef if the encoding cannot be identified. =item B($codepage) Translate windows $codepage into ISO code. The $codepage is numeric or a hex string like '0x0304'. =item B( [$codepage] ) Returns the ISO string for the Microsoft codepage locale. Might return C/false. By default, the actual codepage is used. =item B($iso) Returns the numeric value of the codepage. The $iso may look like this: C. Then, first the C is looked-up. If that does not exist, C is tried. =item B() Returns the numeric language ID for the current codepage language. For example, the numeric value for C<0x0409> for C, and C<0x0411> for C. Returns false if the codepage cannot be identified. =item B() Returns the numeric language ID for the installed codepage language. This is like L, but refers to the codepage that was the default when Windows was first installed. =item B() Returns the locale setting from the control panel. =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Extract.pod0000644000175000001440000000654015000465535022434 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Extract - Collect translatable strings =head1 INHERITANCE Log::Report::Extract is extended by Log::Report::Extract::PerlPPI Log::Report::Extract::Template =head1 SYNOPSIS # See the extensions =head1 DESCRIPTION This module helps maintaining the POT files, updating the list of message-ids which are kept in them. After initiation, the L method needs to be called with all files which changed since last processing and the existing PO files will get updated accordingly. If no translations exist yet, one C file will be created. =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Extract-EB(%options) -Option --Default charset 'utf-8' lexicon =over 2 =item charset => STRING The character-set used in the PO files. =item lexicon => DIRECTORY The place where the lexicon is kept. When no lexicon is defined yet, this will be the directory where an C file will be created. =back =back =head2 Accessors =over 4 =item $obj-EB($domain, $pot, %options) =item $obj-EB() Returns the character-set used inside the POT files. =item $obj-EB() Returns a sorted list of all known domain names. =item $obj-EB() Returns the L object, which is listing the files in the lexicon directory tree. =item $obj-EB($domain) Returns the list of L objects which contain the tables for $domain. =back =head2 Processors =over 4 =item $obj-EB(%options) Remove all references. -Option--Default keep [] =over 2 =item keep => HASH|ARRAY Keep the information about these filename, either specified as ARRAY of names, or a HASH where the keys are the named. =back =item $obj-EB($filename, %options) Update the domains mentioned in the $filename. All text-domains defined in the file will get updated automatically, but should not written before all files are processed. Returned is the number of messages found in this particular file. =item $obj-EB( [$domains] ) Show a status about the DOMAIN (by default all domains). At least mode verbose is required to see this. The statistics are sent to (Log::Report) dispatchers which accept notice and info. This could be syslog. When you have no explicit dispatchers in your program, the level of detail get controlled by the 'mode': use Log::Report mode => 'DEBUG'; # or 'VERBOSE' =item $obj-EB( $domain, $filename, $linenr, $context, $msg, [$msg_plural] ) Register the existence of a ($msg, $msg_plural) in all POTs of the $domain. =item $obj-EB( [$domain] ) Update the information of the files related to $domain, by default all processed DOMAINS. All information known about the written $domain is removed from the cache. =back =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/lib/Log/Report/Lexicon.pod0000644000175000001440000000364015000465535022421 0ustar00markovusers00000000000000=encoding utf8 =head1 NAME Log::Report::Lexicon - translation component of Log::Report =head1 SYNOPSIS =head1 DESCRIPTION This module is the main extry point for the distribution, but has currently no further use. This distribution contains all components of L which handle translations. If you do not need translations, you do not need to install this module. When you use L and need to add translations, it may be very little work: when you nicely wrote texts in the advised message format like print __x"Greetings to you, {name}", name => $name; fault __x"cannot open file {filename}", filename => $fn; then all is in perfect condition to introduce translations: it requires very little to no additions to the existing code! In this distribution: =over 4 =item * L Logic used by the F binary (also included here) to extract msgid's from perl scripts and (website) templates. =item * L Translation table administration, in PO or MO format. =item * L Translation table file file administration, understanding locales, domains, and attributes in the filenames. =item * L The run-time component of translations. =back =head1 METHODS =head2 Constructors =over 4 =item Log::Report::Lexicon-EB(%options) =back =head2 Accessors =head1 SEE ALSO This module is part of Log-Report-Lexicon distribution version 1.12, built on April 18, 2025. Website: F =head1 LICENSE Copyrights 2007-2025 by [Mark Overmeer ]. For other contributors see ChangeLog. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See F Log-Report-Lexicon-1.12/README0000644000175000001440000000150414775703616016444 0ustar00markovusers00000000000000=== README for Log-Report-Lexicon version 1.12 = Generated on Thu Apr 10 10:55:42 2025 by OODoc 2.03 There are various ways to install this module: (1) if you have a command-line, you can do: perl -MCPAN -e 'install ' (2) if you use Windows, have a look at http://ppm.activestate.com/ (3) if you have downloaded this module manually (as root/administrator) gzip -d Log-Report-Lexicon-1.12.tar.gz tar -xf Log-Report-Lexicon-1.12.tar cd Log-Report-Lexicon-1.12 perl Makefile.PL make # optional make test # optional make install For usage, see the included manual-pages or http://search.cpan.org/dist/Log-Report-Lexicon-1.12/ Please report problems to http://rt.cpan.org/Dist/Display.html?Queue=Log-Report-Lexicon Log-Report-Lexicon-1.12/Makefile.PL0000644000175000001440000000365214777242251017537 0ustar00markovusers00000000000000use ExtUtils::MakeMaker; use 5.010; my $version = '1.12'; my %prereq = ( Test::More => '0.86' , Data::Dumper => 0 , Fcntl => 0 , File::Basename => 0 , File::Find => 0 , File::Spec => 0 , File::Temp => 0 , IO::File => 0 , List::Util => 0 , Scalar::Util => 0 , Log::Report => 1.22 , Pod::Usage => 0 , POSIX => 0 , Encode => 0 # sometimes needed, dependencies too large # PPI # Locale::gettext ); $prereq{ 'Win32::TieRegistry' } = 0.24 if $^O eq 'MSWin32'; WriteMakefile ( NAME => 'Log::Report::Lexicon' , VERSION => $version , PREREQ_PM => \%prereq , EXE_FILES => [ 'bin/xgettext-perl' ] , AUTHOR => 'Mark Overmeer ' , ABSTRACT => 'Log::Report translation table management' , LICENSE => 'perl_5' , META_MERGE => { 'meta-spec' => { version => 2 } , resources => { repository => { type => 'git' , url => 'https://github.com/markov2/perl5-Log-Report-Lexicon.git' , web => 'https://github.com/markov2/perl5-Log-Report-Lexicon' } , homepage => 'http://perl.overmeer.net/CPAN/' , license => [ 'http://dev.perl.org/licenses/' ] } } ); sub MY::postamble { <<'__POSTAMBLE' } # for OODoc's oodist, DIST RAWDIR = ../public_html/log-report-lexicon/raw DISTDIR = ../public_html/log-report-lexicon/source # for OODoc's oodist, POD FIRST_YEAR = 2007 EMAIL = markov@cpan.org WEBSITE = http://perl.overmeer.net/CPAN/ EXTENDS = ../Log-Report-Optional:../String-Print:../Log-Report:../Log-Report-Template __POSTAMBLE # for translation tables #linkext:: # bin/xgettext-perl --mode=VERBOSE -p lib/Log/Report/messages lib bin Log-Report-Lexicon-1.12/bin/0000755000175000001440000000000015000465541016314 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/bin/xgettext-perl0000644000175000001440000001463014775703615021076 0ustar00markovusers00000000000000#!/usr/bin/env perl # implements xgettext for Log::Report only, using Log::Report::Extract::PPI # Options like GNU's xgettext use warnings; use strict; use Log::Report 'log-report'; use Getopt::Long qw/:config no_ignore_case bundling/; use File::Find qw/find/; use Pod::Usage qw/pod2usage/; my $lang = 'perl'; my $version = 0; my $help = 0; my $cleanup = 1; my ($from, $output, $fn_match, %configs); my ($char_in, $char_out) = ('utf-8', 'utf-8'); my ($default_domain, $mode, $template); GetOptions 'cleanup!' => $cleanup # kill transl from removed files, def true , 'config|c=s' => \%configs # domain configurations , 'domain|d=s' => \$default_domain # for templates , 'files-from|f=s' => \$from # file with filenames (MANIFEST?) or '-' , 'files-match|m=s' => \$fn_match # select filename is dir , 'from-code=s' => \$char_in , 'help|h' => \$help , 'language|L=s' => \$lang , 'mode=s' => \$mode , 'output-dir|p=s' => \$output , 'template|t=s' => \$template # pattern in ::Template , 'to-code=s' => \$char_out # missing in xgettext? , 'verbose=i' => \$mode , 'version|V' => \$version , 'v+' => \$mode or pod2usage(1); if($version) { print "Log::Report $Log::Report::VERSION\n"; exit 0; } $help && podusage(0); # Load domain information, for instance defining context_rules. The # definitions are global, so automatically find their way in the Log::Report # internals. # --config domain1=filename domain2=filename # --config domain1=filename --config domain2=filename while(my ($domain, $fn) = each %configs) { trace "configuring domain $domain from $fn"; textdomain $domain, config => $fn; } # all output to stderr dispatcher FILE => stderr => to => \*STDERR , mode => $mode, format => sub {shift}; dispatcher close => 'default'; $template || $lang eq 'perl' or error __x"programming language {lang} not supported", lang => $lang; defined $output or error __"explicit output directory (-p) required"; -d $output or mkdir $output or fault __x"cannot create output directory {dir}", dir => $output; my @filenames; if(defined $from) { !@ARGV or error __x"do not combine command-line filenames with --files-from"; if($from eq '-') { @filenames = ; } else { open FILENAMES, '<:raw', $from or fault __x"cannot read filename list from {fn}", fn => $from; @filenames = ; close FILENAMES; } chomp(@filenames); } elsif(@ARGV) { find sub { push @filenames, $File::Find::name if -f }, @ARGV; } else { error "give --files-from or directories to be processed"; } my $extr; my %processed; if($template) { # process from template eval "require Log::Report::Extract::Template"; panic $@ if $@; $default_domain or error __x"specify a text-domain (-d) for the templates"; $extr = Log::Report::Extract::Template->new ( lexicon => $output , charset => $char_out , domain => $default_domain , pattern => $template ); $fn_match ||= qr/\.tt2?$/i; foreach my $filename (@filenames) { unless($filename =~ $fn_match) { info __x"skipping (not a template) {fn}", fn => $filename; next; } $extr->process($filename, charset => $char_in); $processed{$filename}++; } } else { # process the pm files eval "require Log::Report::Extract::PerlPPI"; error $@ if $@; $extr = Log::Report::Extract::PerlPPI->new ( lexicon => $output , charset => $char_out ); $fn_match ||= qr/\.p[lm]$/i; foreach my $filename (@filenames) { unless($filename =~ $fn_match) { info __x"skipping (not perl) {fn}", fn => $filename; next; } $extr->process($filename, charset => $char_in); $processed{$filename}++; } } $extr->cleanup(keep => \%processed) if $cleanup; $extr->showStats; $extr->write; __END__ =head1 NAME xgettext-perl - extract translatable strings =head1 SYNOPSIS xgettext-perl [GENERIC OPTIONS][SCRIPT OPTIONS] directories xgettext-perl [GENERIC OPTIONS][TEMPLATE OPTIONS] directories GENERIC OPTIONS --config -c %config domain configuration --files-from -f $filename source of filenames to be processed --from-code $charset used by input files (default utf-8) --no-cleanup keep unprocessed files in po table --output-dir -p $directory location of lexicons (required) --to-code $charset charset of po files (default utf-8) --version -V show version of this script --verbose=3 -v -vv -vvv debug mode TEMPLATE OPTIONS --domain -d $domain domain to be used --template -t $notation how to recognize the strings to be taken --files-match -m $regex filter filenames, default .pm and .pl SCRIPT OPTIONS --language -L $proglang programming language syntax (now only perl) --files-match -m $regex filter filenames, default .tt and .tt2 =head1 DESCRIPTION This script will maintain PO-files: translation files. On the moment, the number of syntaxes is quite limited (see below) There is no restrain on syntaxes which can be supported: there just was no practical use to implement it yet. =head2 Complex options =over 4 =item --config %config Log::Report translations supports complex additional features, like context sensitive translations, which require a configuration file. See Log::Report::Context Say, your scripts and templates use textdomain name-spaces C and C (plase use better names), then you can pass their respective configuration files as: --config domain1=filename domain2=filename # or --config domain1=filename --config domain2=filename =item --cleanup --no-cleanup You should scan all script or template files in one go, because PO records from files which are not mentioned will get removed. That's the clean-up. However, when you need more scans for a full update, you need to use this option. This also implies possible polution of your translation tables. =back =head2 Extracting from Perl with Log::Report syntax When no --template notation is given, the provided file-names are expected to contain program text. Only Perl5 programs using the Log::Report msgid notation (with leading '__' to mean gettext) =head2 Extracting from Template::Toolkit See L =cut Log-Report-Lexicon-1.12/ChangeLog0000644000175000001440000001035115000465534017320 0ustar00markovusers00000000000000 ==== version history of Log::Report::Lexicon Unless noted otherwise, these changes where initiated and applied by Mark Overmeer. bugs: - with multiple domains extracted at once, remove references which appear in one PO but the file moved to other domain, not scanned in the same xgettext-perl run. - manual-page for xgettext-perl is missing - xgettext-perl does not yet support __*p* introduced by Log::Report version 1.22. It should probably accept variables as $msgctxt argument, to make code maintainable. wishlist: - let xgettext-perl produce mo files as well. version 1.12: Fri 18 Apr 17:10:11 CEST 2025 Fixes: - binmode for POTcompact - confusion between option name 'lexicon' or 'lexicons', accept both. - find gmo files. - no translations for the declaration of the conversion routines in Log::Report Improvements: - add .gitignore - add translation table - extract Template understand FILTER version 1.11: Thu Mar 22 23:32:11 CET 2018 Release 1.10 seems to got lost on CPAN. Upload a new one. Fixes: - fix metadata [Mohammad S Anwar] - $fh->binmode not supported before 5.12 [cpantesters] version 1.10: Tue Jan 23 23:03:57 CET 2018 Improvements: - typo, rt.cpan.org#123008 [Lukas Mai] - additional filename test in 30index.t - add debugging of fn0 to t/30index.t - convert to GIT - publish on GitHUB version 1.09: Mon 28 Aug 10:37:30 CEST 2017 Fixes: - support PO-table charsets only available in the file's header Reported by [Lars Dɪᴇᴄᴋᴏᴡ] Improvements: - do not require Plural-Forms in the header when it is not used. - remove option Log::Report::Lexicon::MOTcompact::read(take_all) Always take all, don't be smart (because it wasn't efficient at all) - support filename extension .gmo as alternative to .mo - ::Lexicon::POT::new(charset) from required to default 'UTF-8' version 1.08: Thu 29 Jun 15:02:15 CEST 2017 Fixes: - textdomain() from package. version 1.07: Tue 27 Jun 16:43:28 CEST 2017 Changes: - interpolated context values require "_context." prefix Fixes: - Accept END with blanks around it in piped syntax. Improvements: - spell-fix rt.cpan.org#118560 [Gregor Herrmann, Debian] - receive lexicon specific code from Log::Report::Translator (part of the Log::Report distribution) - warn when the msgid contains html-encoded characters, while extracting msgids from HTML. version 1.06: Wed 21 Sep 17:10:58 CEST 2016 Fixes: - extract __nx as well. Improvements: - take domain from Dancer2::Plugin::LogReport as well [Andy Beverley] version 1.05: Tue 12 Apr 15:09:32 CEST 2016 Fixes: - charset in MO files. [Paulo A Ferreira] Improvements: - explain context settings for interpolation (new in Log::Report 1.10) - explain relation to gettext's pgettext() command. - added --no-cleanup to bin/xgettext-perl - some documentation for bin/xgettext-perl version 1.04: Mon Jun 15 17:34:38 CEST 2015 Improvements: - spell-fix rt.cpan.org#96465 [Gregor Herrmann, Debian] - add Log::Report::Extract::addPot() version 1.03: Wed Jun 4 17:22:19 CEST 2014 Fixes: - more than one po-file with contexts. Reported by [Richard Still] Improvements: - accept PO-files which have CRLF endings, while running on LF platform (UNIX/Linux) version 1.02: Mon Mar 10 16:08:59 CET 2014 Changes: - ::PO::unused() -> ::PO::useless() Fixes: - do not run t/12ctxt.t on openbsd: that platform does not support LC_ALL [cpantesters] - ::POT::write(only_active) was not documented and only partially implemented [Patrick Goldmann] - ::POT::write() did not check the number of plural forms. [Patrick Goldmann] Improvements: - changed documentation style - explain how to use templates in combination with translation contexts version 1.01: Mon Jan 6 22:42:22 CET 2014 Fixes: - t/12ctxt.t failed when 'en' locale was not installed. Patched by [Slaven Rezic] version 1.00: Sun Jan 5 17:30:43 CET 2014 Split-off from Log::Report Fixes: - remove references to files which have disappeared from the set. Improvements: - add ::Lexicon as main extry point - add ::Translator::Context and smart support for msgctxt - command-line parameter option in xgettext-perl - add msgctxt support to tables maintained in POT, POTcompact, and MOcompact format. - more documentation Log-Report-Lexicon-1.12/xt/0000755000175000001440000000000015000465541016177 5ustar00markovusers00000000000000Log-Report-Lexicon-1.12/xt/99pod.t0000644000175000001440000000041614775703615017350 0ustar00markovusers00000000000000#!/usr/bin/env perl use warnings; use strict; use Test::More; BEGIN { eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; plan skip_all => "devel home uses OODoc" if $ENV{MARKOV_DEVEL}; } all_pod_files_ok(); Log-Report-Lexicon-1.12/META.yml0000644000175000001440000000171315000465541017017 0ustar00markovusers00000000000000--- abstract: 'Log::Report translation table management' author: - 'Mark Overmeer ' build_requires: ExtUtils::MakeMaker: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Log-Report-Lexicon no_index: directory: - t - inc requires: Data::Dumper: '0' Encode: '0' Fcntl: '0' File::Basename: '0' File::Find: '0' File::Spec: '0' File::Temp: '0' IO::File: '0' List::Util: '0' Log::Report: '1.22' POSIX: '0' Pod::Usage: '0' Scalar::Util: '0' Test::More: '0.86' resources: homepage: http://perl.overmeer.net/CPAN/ license: http://dev.perl.org/licenses/ repository: https://github.com/markov2/perl5-Log-Report-Lexicon.git version: '1.12' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Log-Report-Lexicon-1.12/META.json0000644000175000001440000000326115000465541017167 0ustar00markovusers00000000000000{ "abstract" : "Log::Report translation table management", "author" : [ "Mark Overmeer " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Log-Report-Lexicon", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "Data::Dumper" : "0", "Encode" : "0", "Fcntl" : "0", "File::Basename" : "0", "File::Find" : "0", "File::Spec" : "0", "File::Temp" : "0", "IO::File" : "0", "List::Util" : "0", "Log::Report" : "1.22", "POSIX" : "0", "Pod::Usage" : "0", "Scalar::Util" : "0", "Test::More" : "0.86" } } }, "release_status" : "stable", "resources" : { "homepage" : "http://perl.overmeer.net/CPAN/", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "https://github.com/markov2/perl5-Log-Report-Lexicon.git", "web" : "https://github.com/markov2/perl5-Log-Report-Lexicon" } }, "version" : "1.12", "x_serialization_backend" : "JSON::PP version 4.16" }