all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH proxmox-i18n 1/2] format with proxmox-perltidy
@ 2025-07-31 11:25 Maximiliano Sandoval
  2025-07-31 11:26 ` [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support Maximiliano Sandoval
  2025-07-31 12:01 ` [pve-devel] applied: [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Thomas Lamprecht
  0 siblings, 2 replies; 4+ messages in thread
From: Maximiliano Sandoval @ 2025-07-31 11:25 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---
 po2js.pl | 69 ++++++++++++++++++++++++++------------------------------
 1 file changed, 32 insertions(+), 37 deletions(-)

diff --git a/po2js.pl b/po2js.pl
index ee5bbd1..c5268d7 100755
--- a/po2js.pl
+++ b/po2js.pl
@@ -26,14 +26,10 @@ sub fnv31a {
     my $hval = 0x811c9dc5;
 
     foreach my $c (unpack('C*', $string)) {
-	$hval ^= $c;
-	$hval += (
-	    (($hval << 1) ) +
-	    (($hval << 4) ) +
-	    (($hval << 7) ) +
-	    (($hval << 8) ) +
-	    (($hval << 24) ) );
-	$hval = $hval & 0xffffffff;
+        $hval ^= $c;
+        $hval +=
+            ((($hval << 1)) + (($hval << 4)) + (($hval << 7)) + (($hval << 8)) + (($hval << 24)));
+        $hval = $hval & 0xffffffff;
     }
     return $hval & 0x7fffffff;
 }
@@ -41,51 +37,50 @@ sub fnv31a {
 my $catalog = {};
 
 foreach my $filename (@ARGV) {
-    my $href = Locale::PO->load_file_ashash($filename) ||
-	die "unable to load '$filename'\n";
-    
+    my $href = Locale::PO->load_file_ashash($filename)
+        || die "unable to load '$filename'\n";
+
     my $charset;
     my $hpo = $href->{'""'} || die "no header";
     my $header = $hpo->dequote($hpo->msgstr);
     if ($header =~ m|^Content-Type:\s+text/plain;\s+charset=(\S+)$|im) {
-	$charset = $1;
+        $charset = $1;
     } else {
-	die "unable to get charset\n" if !$charset;
+        die "unable to get charset\n" if !$charset;
     }
 
-
     foreach my $k (keys %$href) {
-	my $po = $href->{$k};
-	next if $po->fuzzy(); # skip fuzzy entries
-	my $ref = $po->reference();
+        my $po = $href->{$k};
+        next if $po->fuzzy(); # skip fuzzy entries
+        my $ref = $po->reference();
 
-	# skip unused entries
-	next if !$ref;
+        # skip unused entries
+        next if !$ref;
 
-	# skip entries if t is defined (pve/pmg) and the string is
-	# not used there or in the widget toolkit
-	next if $options->{t} && $ref !~ m/($options->{t}|proxmox)\-/;
-    
-	my $qmsgid = decode($charset, $po->msgid);
-	my $msgid = $po->dequote($qmsgid);
+        # skip entries if t is defined (pve/pmg) and the string is
+        # not used there or in the widget toolkit
+        next if $options->{t} && $ref !~ m/($options->{t}|proxmox)\-/;
 
-	my $qmsgstr = decode($charset, $po->msgstr);
-	my $msgstr = $po->dequote($qmsgstr);
+        my $qmsgid = decode($charset, $po->msgid);
+        my $msgid = $po->dequote($qmsgid);
 
-	next if !length($msgid); # skip header
-	
-	next if !length($msgstr); # skip untranslated entries
+        my $qmsgstr = decode($charset, $po->msgstr);
+        my $msgstr = $po->dequote($qmsgstr);
 
-	my $digest = fnv31a($msgid);
+        next if !length($msgid); # skip header
 
-	die "duplicate digest" if $catalog->{$digest};
+        next if !length($msgstr); # skip untranslated entries
 
-	$catalog->{$digest} = [ $msgstr ];
-	# later, we can add plural forms to the array
+        my $digest = fnv31a($msgid);
+
+        die "duplicate digest" if $catalog->{$digest};
+
+        $catalog->{$digest} = [$msgstr];
+        # later, we can add plural forms to the array
     }
 }
 
-my $json = to_json($catalog, {canonical => 1, utf8 => 1});
+my $json = to_json($catalog, { canonical => 1, utf8 => 1 });
 
 my $version = $options->{v} // ("dev-build " . localtime());
 my $content = "// $version\n"; # write version to the beginning to better avoid stale cache
@@ -120,8 +115,8 @@ function gettext(buf) {
 __EOD
 
 if ($outfile) {
-    open(my $fh, '>', $outfile) ||
-	die "unable to open '$outfile' - $!\n";
+    open(my $fh, '>', $outfile)
+        || die "unable to open '$outfile' - $!\n";
     print $fh $content;
 } else {
     print $content;
-- 
2.47.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support
  2025-07-31 11:25 [pve-devel] [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Maximiliano Sandoval
@ 2025-07-31 11:26 ` Maximiliano Sandoval
  2025-07-31 11:37   ` Maximiliano Sandoval
  2025-07-31 12:01 ` [pve-devel] applied: [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Thomas Lamprecht
  1 sibling, 1 reply; 4+ messages in thread
From: Maximiliano Sandoval @ 2025-07-31 11:26 UTC (permalink / raw)
  To: pve-devel

We add new array for plural forms.

We use the msgid as the identifier translatable strings with a plural
form. This is the same as done for .mo files, from [1]:

> However, only the singular of the original string takes part in the
> hash table lookup.

We also obtain the plural forms and the number of plural forms directly
from the $LANG.po file, this requires the file to have a Plural-Forms
header that is valid, otherwise it would fail to evaluated by the
ngettext function when calling `Number($plural_forms)`.

Regarding the fallback to the original string, from [2]:

> The parameter n is used to determine the plural form. If no message
> catalog is found msgid1 is returned if n == 1, otherwise msgid2.

We default to the non-translated singular/plural depending on whether
n==1.

Regarding the default for $nplurals and $plural_forms: We select the
values for "Two forms, singular used for one only" from [2], this would
match with germanic and latin families.

This partially restores commit 2235ad7c7c79e29f09d3f3205ad86e862b5ac14c.

[1] https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
[2] https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---
 po2js.pl | 52 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 45 insertions(+), 7 deletions(-)

diff --git a/po2js.pl b/po2js.pl
index c5268d7..d5a78a6 100755
--- a/po2js.pl
+++ b/po2js.pl
@@ -35,6 +35,10 @@ sub fnv31a {
 }
 
 my $catalog = {};
+my $plurals_catalog = {};
+
+my $nplurals = 2;
+my $plural_forms = "n!=1";
 
 foreach my $filename (@ARGV) {
     my $href = Locale::PO->load_file_ashash($filename)
@@ -49,6 +53,11 @@ foreach my $filename (@ARGV) {
         die "unable to get charset\n" if !$charset;
     }
 
+    if ($header =~ m|^Plural-Forms:\s+nplurals\s?=\s?([123456]);\s+plural=(.*);$|im) {
+	$nplurals = $1 + 0;
+	$plural_forms = $2;
+    }
+
     foreach my $k (keys %$href) {
         my $po = $href->{$k};
         next if $po->fuzzy(); # skip fuzzy entries
@@ -64,23 +73,37 @@ foreach my $filename (@ARGV) {
         my $qmsgid = decode($charset, $po->msgid);
         my $msgid = $po->dequote($qmsgid);
 
-        my $qmsgstr = decode($charset, $po->msgstr);
-        my $msgstr = $po->dequote($qmsgstr);
+        my $qmsgid_plural = decode($charset, $po->msgid_plural);
+        my $msgid_plural = $po->dequote($qmsgid_plural);
 
-        next if !length($msgid); # skip header
-
-        next if !length($msgstr); # skip untranslated entries
+        next if !length($msgid) && !length($msgid_plural); # skip header
 
         my $digest = fnv31a($msgid);
 
         die "duplicate digest" if $catalog->{$digest};
 
-        $catalog->{$digest} = [$msgstr];
-        # later, we can add plural forms to the array
+        if (defined($po->msgstr)) {
+            my $qmsgstr = decode($charset, $po->msgstr);
+            my $msgstr = $po->dequote($qmsgstr);
+
+            next if !length($msgstr); # skip untranslated entries
+            $catalog->{$digest} = [$msgstr];
+        }
+
+        if (defined(my $plurals = $po->msgstr_n)) {
+            for my $case (sort { $a <=> $b } keys $plurals->%*) {
+                my $qmsgstr_n = decode($charset, $plurals->{$case});
+                my $msgstr_n = $po->dequote($qmsgstr_n);
+
+                next if !length($msgstr_n); # skip untranslated entries
+                push $plurals_catalog->{$digest}->@*, $msgstr_n;
+            }
+        }
     }
 }
 
 my $json = to_json($catalog, { canonical => 1, utf8 => 1 });
+my $plurals_json = to_json($plurals_catalog, { canonical => 1, utf8 => 1 });
 
 my $version = $options->{v} // ("dev-build " . localtime());
 my $content = "// $version\n"; # write version to the beginning to better avoid stale cache
@@ -91,6 +114,7 @@ $content .= "// Proxmox Message Catalog: $outfile\n" if $outfile;
 
 $content .= <<__EOD;
 __proxmox_i18n_msgcat__ = $json;
+__proxmox_i18n_plurals_msgcat__ = $plurals_json;
 
 function fnv31a(text) {
     var len = text.length;
@@ -112,6 +136,20 @@ function gettext(buf) {
     }
     return data[0] || buf;
 }
+
+function ngettext(singular, plural, n) {
+    const msg_idx = Number($plural_forms);
+    const digest = fnv31a(singular);
+    const translation = __proxmox_i18n_plurals_msgcat__[digest];
+    if (!translation[msg_idx]) {
+        if (n === 1) {
+	    return singular;
+        } else {
+	    return plural;
+        }
+    }
+    return translation[msg_idx];
+}
 __EOD
 
 if ($outfile) {
-- 
2.47.2



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support
  2025-07-31 11:26 ` [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support Maximiliano Sandoval
@ 2025-07-31 11:37   ` Maximiliano Sandoval
  0 siblings, 0 replies; 4+ messages in thread
From: Maximiliano Sandoval @ 2025-07-31 11:37 UTC (permalink / raw)
  To: pve-devel

Maximiliano Sandoval <m.sandoval@proxmox.com> writes:

> @@ -112,6 +136,20 @@ function gettext(buf) {
>      }
>      return data[0] || buf;
>  }
> +
> +function ngettext(singular, plural, n) {
> +    const msg_idx = Number($plural_forms);
> +    const digest = fnv31a(singular);
> +    const translation = __proxmox_i18n_plurals_msgcat__[digest];
> +    if (!translation[msg_idx]) {

This should be:

       if (!translation || msg_idx >= translation.length) {

or similar. It is possible a message might be missing translations for
certain plural forms.

> +        if (n === 1) {
> +	    return singular;
> +        } else {
> +	    return plural;
> +        }
> +    }
> +    return translation[msg_idx];
> +}
>  __EOD
>  
>  if ($outfile) {

-- 
Maximiliano


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [pve-devel] applied: [PATCH proxmox-i18n 1/2] format with proxmox-perltidy
  2025-07-31 11:25 [pve-devel] [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Maximiliano Sandoval
  2025-07-31 11:26 ` [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support Maximiliano Sandoval
@ 2025-07-31 12:01 ` Thomas Lamprecht
  1 sibling, 0 replies; 4+ messages in thread
From: Thomas Lamprecht @ 2025-07-31 12:01 UTC (permalink / raw)
  To: pve-devel, Maximiliano Sandoval

On Thu, 31 Jul 2025 13:25:59 +0200, Maximiliano Sandoval wrote:
> 

Worked here and I could now change all sites to consistently use plural variant
of Warnings, which is currently the only label that caused conflicts.

That said, if we extend usage and always need to change all repos of the
previous singular-only use sites it might get a bit of a PITA...

Anyhow: Applied, thanks!

[1/2] format with proxmox-perltidy
      commit: cef9669fb45fedc041e82a910020d2afaf288b68
[2/2] po2js: add ngettext support
      commit: 49573acf7fda7c9421c8eba2ec7c5942ae07915e


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2025-07-31 12:00 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-31 11:25 [pve-devel] [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Maximiliano Sandoval
2025-07-31 11:26 ` [pve-devel] [PATCH proxmox-i18n 2/2] po2js: add ngettext support Maximiliano Sandoval
2025-07-31 11:37   ` Maximiliano Sandoval
2025-07-31 12:01 ` [pve-devel] applied: [PATCH proxmox-i18n 1/2] format with proxmox-perltidy Thomas Lamprecht

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal