summaryrefslogtreecommitdiff
path: root/util/jsgen.pl
blob: ae0a9402e5b6fd6beecfdb13c1255dec4553da37 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/perl

package VNDB;

use strict;
use warnings;
use Encode 'encode_utf8';
use Cwd 'abs_path';
use JSON::XS;
eval { require JavaScript::Minifier::XS; };

our($ROOT, %S, %O);
BEGIN { ($ROOT = abs_path $0) =~ s{/util/jsgen\.pl$}{}; }
require $ROOT.'/data/global.pl';

use lib "$ROOT/lib";
use LangFile;

# The VNDB::L10N module is not really suited to be used outside the VNDB::*
# framework, but it's the central location that defines which languages we have
# and in what order to display them.
use VNDB::L10N;



my %lang; # lang1 => { key1 => .., key22 => .. }, lang2 => { .. }

sub l10n_load {
  # fetch all text from lang.txt
  my $lang = LangFile->new(read => "$ROOT/data/lang.txt");
  my $key;
  while((my $l = $lang->read())) {
    my $type = shift @$l;
    $key = shift @$l if $type eq 'key';
    $lang{$l->[0]}{$key} = $l->[2] if $type eq 'tl';
  }
}


# Remove formatting codes from L10N strings that the Javascript mt() does not support.
#   [quant,_n,x,..] -> x
#   [index,_n,x,..] -> x
# [_n] is supported by Javascript mt(). It is left alone if no arguments are
# given, otherwise it is replaced. All other codes result in an error.
sub l10nstr {
  my($lang, $key, @args) = @_;
  local $_ = $lang{$lang}{$key} || $lang{'en'}{$key} || '';

  # Simplify parsing
  s/~\[/JSGEN_QBEGIN/g;
  s/~]/JSGEN_QENDBR/g;
  s/~,/JSGEN_QCOMMA/g;

  # Replace quant/index
  s/\[(?:quant|index),_[0-9]+,([^,\]]*)[^\]]*\]/$1/g;

  # Replace [_n]
  for my $i (0..$#args) {
    my $v = $i+1;
    s/\[_$v\]/$args[$i]/g;
  }

  # Check for unhandled codes
  die "Unsupported formatting code in $lang:$key\n" if /\[[^_]/;

  # Convert back
  s/JSGEN_QBEGIN/~[/g;
  s/JSGEN_QENDBR/~]/g;
  s/JSGEN_QCOMMA/,/g; # No need to escape, at this point there are no codes with arguments
  $_;
}


sub l10n {
  my($lang, $js) = @_;

  # parse the .js code and replace mt()'s that can be modified in-place, otherwise add to the @keys
  my @keys;
  $js =~ s{(?:mt\('([a-z0-9_]+)'([,\)])|l10n /([^/]+)/)}#
    my($k, $s, $q) = ($1, $2, $3);
    my $v = $k && l10nstr($lang, $k);
    if($q) {
      $q ne '<perl regex>' && push @keys, qr/$q/; ''
    } elsif($s eq ')' && $v && $v !~ /[\~\[\]]/) {
      $v =~ s/"/\\"/g;
      $v =~ s/\n/\\n/g;
      qq{"$v"}
    } else {
      push @keys, '^'.quotemeta($k).'$';
      "mt('$k'$s"
    }
  #eg;

  my %keys;
  for my $key (sort keys %{$lang{$lang}}) {
    next if !grep $key =~ /$_/, @keys;
    $keys{$key} = l10nstr($lang, $key);
  }
  (\%keys, $js);
}


# screen resolution information, suitable for usage in filFSelect()
sub resolutions {
  my $ln = shift;
  my $cat = '';
  my @r;
  my $push = \@r;
  for my $i (0..$#{$S{resolutions}}) {
    my $r = $S{resolutions}[$i];
    if($cat ne $r->[1]) {
      push @r, [$r->[1] =~ /^_/ ? l10nstr($ln, $r->[1]) : $r->[1]];
      $cat = $r->[1];
      $push = $r[$#r];
    }
    my $n = $r->[0] =~ /^_/ ? l10nstr($ln, $r->[0]) : $r->[0];
    push @$push, [$i, $n];
  }
  \@r
}


sub vars {
  my($lang, $l10n) = @_;
  my %vars = (
    rlist_status  => [ map l10nstr($lang, $_?"_rlist_status_$_":'_unknown'), @{$S{rlist_status}} ],
    cookie_prefix => $O{cookie_prefix},
    age_ratings   => [ map [ $_, l10nstr($lang, $_ == -1 ? ('_unknown') : $_ == 0 ? ('_minage_all') : ('_minage_age', $_)) ], @{$S{age_ratings}} ],
    languages     => [ map [ $_, l10nstr($lang, "_lang_$_") ], @{$S{languages}} ],
    platforms     => [ map [ $_, l10nstr($lang, "_plat_$_") ], @{$S{platforms}} ],
    char_roles    => [ map [ $_, l10nstr($lang, "_charrole_$_") ], @{$S{char_roles}} ],
    media         => [ map [ $_, l10nstr($lang, "_med_$_"), $S{media}{$_} ], sort keys %{$S{media}} ],
    release_types => [ map [ $_, l10nstr($lang, "_rtype_$_") ], @{$S{release_types}} ],
    animated      => [ map [ 1*$_, l10nstr($lang, $_?"_animated_$_":'_unknown' ) ], @{$S{animated}} ],
    voiced        => [ map [ 1*$_, l10nstr($lang, $_?"_voiced_$_":'_unknown' ) ], @{$S{voiced}} ],
    vn_lengths    => [ map [ 1*$_, l10nstr($lang, $_?"_vnlength_$_":'_unknown' ) ], @{$S{vn_lengths}} ],
    blood_types   => [ map [ $_, l10nstr($lang, $_ eq 'unknown' ? '_unknown' : "_bloodt_$_") ], @{$S{blood_types}} ],
    genders       => [ map [ $_, l10nstr($lang, "_gender_$_") ], @{$S{genders}} ],
    staff_roles   => [ map [ $_, l10nstr($lang, "_credit_$_") ], @{$S{staff_roles}} ],
    resolutions   => scalar resolutions($lang),
    l10n_lang     => [ map [ $_, l10nstr($_, "_lang_$_") ], VNDB::L10N::languages() ],
    l10n_str      => $l10n,
  );
  JSON::XS->new->encode(\%vars);
}


# Reads main.js and any included files.
sub readjs {
  my $f = shift || 'main.js';
  open my $JS, '<:utf8', "$ROOT/data/js/$f" or die $!;
  local $/ = undef;
  local $_ = <$JS>;
  close $JS;
  s{^//include (.+)$}{'(function(){'.readjs($1).'})();'}meg;
  $_;
}


sub jsgen {
  my $js = readjs 'main.js';

  for my $l (VNDB::L10N::languages()) {
    my($l10n, $body) = l10n($l, $js);
    $body =~ s{/\*VARS\*/}{vars($l, $l10n)}eg;

    # JavaScript::Minifier::XS doesn't correctly handle perl's unicode, so manually encode
    my $content = encode_utf8($body);
    open my $NEWJS, '>', "$ROOT/static/f/js/$l.js" or die $!;
    print $NEWJS $JavaScript::Minifier::XS::VERSION ? JavaScript::Minifier::XS::minify($content) : $content;
    close $NEWJS;
  }
}

l10n_load;
jsgen;