Commit 96f4cd66 authored by Richard Mansfield's avatar Richard Mansfield

Plural forms support

parent f42823a0
......@@ -39,6 +39,10 @@ define('L_STRINGVALUE', 6);
define('L_HEREDOCSTRING', 7);
define('L_HEREDOCEND', 8);
define('L_SEMI', 9);
define('L_STARTPLURAL', 10);
define('L_NEXTPLURAL', 11);
define('L_PLURALKEY', 12);
define('L_PLURALDOUBLEARROW', 13);
// Reading default en.utf8 langpack files fails unless INTERNAL is defined
define('INTERNAL', 1);
......@@ -59,6 +63,7 @@ function phptopo($en_strings, $fileid, $in, $pot) {
elseif (($state == L_STRING || $state == L_START) && $t[0] == T_VARIABLE && $t[1] == '$string') {
$keys = array();
$values = array();
$plurals = array();
$state = L_LBRACKET;
}
elseif ($state == L_LBRACKET && $t == '[') {
......@@ -85,6 +90,36 @@ function phptopo($en_strings, $fileid, $in, $pot) {
unset($heredoc);
$state = L_HEREDOCSTRING;
}
elseif ($state == L_STRINGVALUE && $t[0] == T_ARRAY) {
$state = L_STARTPLURAL;
}
elseif ($state == L_STARTPLURAL && $t[0] == '(') {
$pluralindex = null;
$state = L_PLURALKEY;
}
elseif ($state == L_PLURALKEY && $t[0] == T_LNUMBER) {
$pluralindex = $t[1];
$state = L_PLURALDOUBLEARROW;
}
elseif ($state == L_PLURALKEY && $t[0] == T_CONSTANT_ENCAPSED_STRING) {
if (!is_null($pluralindex)) {
$plurals[$pluralindex] = $t[1];
}
else {
$plurals[] = $t[1];
}
$state = L_NEXTPLURAL;
}
elseif ($state == L_PLURALDOUBLEARROW && $t[0] == T_DOUBLE_ARROW) {
$state = L_PLURALKEY;
}
elseif ($state == L_NEXTPLURAL && $t[0] == ',') {
$pluralindex = null;
$state = L_PLURALKEY;
}
elseif ($state == L_NEXTPLURAL && $t[0] == ')' || $state == L_PLURALKEY && $t[0] == ')') {
$state = L_SEMI;
}
elseif ($state == L_HEREDOCSTRING && $t[0] == T_ENCAPSED_AND_WHITESPACE) {
$heredoc = $t[1];
$state = L_HEREDOCEND;
......@@ -96,16 +131,69 @@ function phptopo($en_strings, $fileid, $in, $pot) {
$state = L_STRINGVALUE;
}
elseif ($state == L_SEMI && $t == ';') {
if (isset($heredoc)) {
$hdstring = $heredoc;
unset($heredoc);
}
else {
$hdstring = null;
}
foreach ($keys as $key) {
eval('$k = ' . $key . ';');
if (isset($en_strings[$k]) && strlen($en_strings[$k]) > 0) {
$po .= "\n\n#: $fileid $k";
$po .= "\nmsgctxt \"$fileid $k\"";
if (!isset($en_strings[$k])) {
// The file is translating a string we don't care about
continue;
}
if (is_string($en_strings[$k]) && strlen($en_strings[$k]) < 1) {
// An empty string doesn't need translation
continue;
}
if (is_array($en_strings[$k])) {
// Plural forms: English should always have two
if (count($en_strings[$k]) != 2) {
continue;
}
if (!isset($en_strings[$k][0]) || !isset($en_strings[$k][1])) {
continue;
}
// Plurals must be translated as plurals
if (empty($plurals)) {
continue;
}
}
$po .= "\n\n#: $fileid $k";
$po .= "\nmsgctxt \"$fileid $k\"";
if (is_array($en_strings[$k])) {
$po .= "\nmsgid \"" . addcslashes($en_strings[$k][0], "\\\"\r\n") . '"';
$po .= "\nmsgid_plural \"" . addcslashes($en_strings[$k][1], "\\\"\r\n") . '"';
if ($pot) {
$po .= "\nmsgstr[0] \"\"";
$po .= "\nmsgstr[1] \"\"";
}
else {
foreach ($plurals as $pindex => $qstring) {
$po .= "\nmsgstr[$pindex] \"";
eval('$s = ' . $qstring . ';');
$po .= addcslashes($s, "\\\"\r\n");
$po .= '"';
}
}
}
else {
$po .= "\nmsgid \"" . addcslashes($en_strings[$k], "\\\"\r\n") . '"';
$po .= "\nmsgstr \"";
if (!$pot) {
if (isset($heredoc)) {
$po .= addcslashes($heredoc, "\\\"\r\n");
if ($pot) {
$po .= "\nmsgstr \"\"";
}
else {
$po .= "\nmsgstr \"";
if (!is_null($hdstring)) {
$po .= addcslashes($hdstring, "\\\"\r\n");
}
else {
foreach ($values as $qstring) {
......@@ -113,13 +201,10 @@ function phptopo($en_strings, $fileid, $in, $pot) {
$po .= addcslashes($s, "\\\"\r\n");
}
}
$po .= '"';
}
$po .= '"';
unset($en_strings[$k]); // Avoid duplicates
}
if (isset($heredoc)) {
unset($heredoc);
}
unset($en_strings[$k]); // Avoid duplicates
}
$state = L_STRING;
}
......
This diff is collapsed.
......@@ -13,6 +13,7 @@
use File::Path qw(mkpath);
use File::Basename qw(fileparse);
use Locale::PO;
use FindBin;
my ($inputfile, $outputdir, $lang) = @ARGV;
......@@ -25,13 +26,42 @@ my $strings = Locale::PO->load_file_asarray($inputfile);
my %htmlfiles = ();
my %phpfiles = ();
my $plural = undef;
foreach my $po (@$strings) {
my $content = $po->msgstr();
next if ( ! defined $content );
$content =~ s{\\n}{\n}g;
next if ( $content eq '' || $content eq '""' );
my $content_n = $po->msgstr_n();
next if ( ! defined $content && ! defined $content_n );
if ( defined $content ) {
# A normal, non-pluralised translation
$content =~ s{\\n}{\n}g;
next if ( $content eq '' || $content eq '""' );
}
if ( defined $content_n ) {
# A translation with multiple plural forms
my $anything = 0;
foreach my $k ( keys %$content_n ) {
$content_n->{$k} =~ s{\\n}{\n}g;
$anything ||= ($content_n->{$k} ne '' && $content_n->{$k} ne '""');
}
next if ! $anything;
}
my $reference = $po->reference();
next if ( ! defined $reference );
if ( ! defined $reference ) {
# Look for "Plural-Forms:" po header
if ( ! defined $plural ) {
my $msgid = $po->msgid();
next if ( defined $msgid && $msgid ne '""' && $msgid ne '' );
if ( $content =~ m{\nPlural-Forms:\s*nplurals\s*=\s*\d+\s*;\s*plural\s*=\s*(.+?)[\s;]*\n} ) {
$plural = $1;
}
}
next;
}
if ($reference =~ m{^(\S*lang/)\S+\.utf8(/\S+)\.html$}) {
my $filename = $1 . $lang . $2 . '.html';
# $content =~ $po->dequote($content);
......@@ -43,13 +73,26 @@ foreach my $po (@$strings) {
elsif ($reference =~ m{^(\S*lang/)\S+\.utf8(/\S+)\.php\s+(\S+)$}) {
my $key = $3;
my $filename = $1 . $lang . $2 . '.php';
# Output with single quotes or variables get interpolated
if ( $content =~ m{^".*"$}s ) {
$content =~ s{'}{\\'}gs;
$content =~ s{^"(.*)"$}{'$1'}s;
$content =~ s{\\"}{"}gs;
# Some things in langconfig.php are fairly meta, and probably
# shouldn't form part of the translation. For now, just
# remove pluralrule & pluralfunction - we'll overwrite them
# based on the Plural-Forms: header, or the hardcoded list.
if ($2 eq 'langconfig' && ($key eq 'pluralrule' || $key eq 'pluralfunction')) {
next;
}
# Normal non-plural string.
if ( defined $content ) {
$phpfiles{$filename}->{$key}->{msgstr} = fixquotes($content);
next;
}
# Plural forms (in $content_n)
$phpfiles{$filename}->{$key}->{msgstr_n} = {};
foreach my $k ( keys %$content_n ) {
$phpfiles{$filename}->{$key}->{msgstr_n}->{$k} = fixquotes($content_n->{$k});
}
$phpfiles{$filename}->{$key} = $content;
}
}
......@@ -69,9 +112,56 @@ foreach my $phpfile (keys %phpfiles) {
open(my $fh, '>', "$dir/$filename");
print $fh "<?php\n\ndefined('INTERNAL') || die();\n\n";
foreach my $key (sort keys %{$phpfiles{$phpfile}}) {
print $fh "\$string['$key'] = " . $phpfiles{$phpfile}->{$key} . ";\n";
if ( $phpfiles{$phpfile}->{$key}->{msgstr} ) {
print $fh "\$string['$key'] = " . $phpfiles{$phpfile}->{$key}->{msgstr} . ";\n";
}
elsif ( $phpfiles{$phpfile}->{$key}->{msgstr_n} ) {
print $fh "\$string['$key'] = array(\n";
foreach my $k ( sort keys %{$phpfiles{$phpfile}->{$key}->{msgstr_n}} ) {
print $fh " $k => $phpfiles{$phpfile}->{$key}->{msgstr_n}->{$k},\n";
}
print $fh ");\n";
}
}
close $fh;
}
# Write plural forms into langconfig.php
my $langshort = $lang;
$langshort =~ s/^([a-zA-Z_]+)\.utf8/$1/;
if ( ! defined $plural ) {
# If there was no "Plural-Forms:" po header, read plural forms
# rule from hardcoded list in pluralforms.pl
open (my $fh, '<', "perl $FindBin::Bin/pluralforms.pl $langshort");
$plural = <$fh>;
close $fh;
}
if ( defined $plural && $plural =~ m/\S+/ ) {
open(my $fh, '>>', "$outputdir/lang/$lang/langconfig.php");
$plural =~ s{[']+}{}g;
my $pluralphp = $plural;
$pluralphp =~ s{n}{\$n}g;
print $fh <<EOF;
// Plural forms, added by language pack generator
\$string['pluralrule'] = '$plural';
\$string['pluralfunction'] = 'plural_${langshort}_utf8';
function plural_${langshort}_utf8(\$n) {
return $pluralphp;
}
EOF
close $fh;
}
sub fixquotes {
my $content = shift;
# Output with single quotes or variables get interpolated
if ( $content =~ m{^".*"$}s ) {
$content =~ s{'}{\\'}gs;
$content =~ s{^"(.*)"$}{'$1'}s;
$content =~ s{\\"}{"}gs;
}
return $content;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment