| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Jenkins::i18n; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 8 |  |  | 8 |  | 531334 | use 5.014004; | 
|  | 8 |  |  |  |  | 98 |  | 
| 4 | 8 |  |  | 8 |  | 42 | use strict; | 
|  | 8 |  |  |  |  | 16 |  | 
|  | 8 |  |  |  |  | 180 |  | 
| 5 | 8 |  |  | 8 |  | 51 | use warnings; | 
|  | 8 |  |  |  |  | 19 |  | 
|  | 8 |  |  |  |  | 307 |  | 
| 6 | 8 |  |  | 8 |  | 56 | use Carp qw(confess); | 
|  | 8 |  |  |  |  | 33 |  | 
|  | 8 |  |  |  |  | 388 |  | 
| 7 | 8 |  |  | 8 |  | 43 | use File::Find; | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 563 |  | 
| 8 | 8 |  |  | 8 |  | 51 | use File::Spec; | 
|  | 8 |  |  |  |  | 14 |  | 
|  | 8 |  |  |  |  | 218 |  | 
| 9 | 8 |  |  | 8 |  | 3306 | use Set::Tiny; | 
|  | 8 |  |  |  |  | 9186 |  | 
|  | 8 |  |  |  |  | 410 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 8 |  |  | 8 |  | 3287 | use Jenkins::i18n::Properties; | 
|  | 8 |  |  |  |  | 22 |  | 
|  | 8 |  |  |  |  | 276 |  | 
| 12 | 8 |  |  | 8 |  | 3330 | use Jenkins::i18n::FindResults; | 
|  | 8 |  |  |  |  | 25 |  | 
|  | 8 |  |  |  |  | 308 |  | 
| 13 | 8 |  |  | 8 |  | 3334 | use Jenkins::i18n::Assertions qw(is_jelly_file has_empty); | 
|  | 8 |  |  |  |  | 19 |  | 
|  | 8 |  |  |  |  | 614 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | =pod | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | =head1 NAME | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | Jenkins::i18n - functions for the jtt CLI | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | use Jenkins::i18n qw(remove_unused find_files load_properties load_jelly find_langs); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | C is a CLI program used to help translating the Jenkins properties file. | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | This module implements some of the functions used by the CLI. | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | =cut | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 8 |  |  | 8 |  | 53 | use Exporter 'import'; | 
|  | 8 |  |  |  |  | 15 |  | 
|  | 8 |  |  |  |  | 18372 |  | 
| 34 |  |  |  |  |  |  | our @EXPORT_OK = ( | 
| 35 |  |  |  |  |  |  | 'remove_unused', 'find_files', 'load_properties', 'load_jelly', | 
| 36 |  |  |  |  |  |  | 'find_langs',    'all_data',   'dump_keys',       'merge_data', | 
| 37 |  |  |  |  |  |  | 'find_missing' | 
| 38 |  |  |  |  |  |  | ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | our $VERSION = '0.10'; | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =head1 EXPORT | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | None by default. | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =head2 find_missing | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | Compares the keys available from the source (Jelly and/or Properties files) | 
| 51 |  |  |  |  |  |  | with the i18n file and updates the statistics based on the B, | 
| 52 |  |  |  |  |  |  | i.e., the keys that exists in the source but not in the i18n Properties file. | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | Expects as parameters the following: | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =over | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | =item 1. | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | a hash reference with all the keys/values from the Jelly/Properties file. | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | =item 2. | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | a hash reference with all the keys/values from the i18n Properties file. | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | =item 3. | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | a instance of a L class. | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =item 4. | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | a instance of L class. | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | =back | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =cut | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | sub find_missing { | 
| 79 | 2 |  |  | 2 | 1 | 12 | my ( $source_ref, $i18n_ref, $stats, $warnings ) = @_; | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 2 |  |  |  |  | 4 | foreach my $entry ( keys %{$source_ref} ) { | 
|  | 2 |  |  |  |  | 6 |  | 
| 82 | 6 |  |  |  |  | 20 | $stats->add_key($entry); | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | # TODO: skip increasing missing if operation is to delete those | 
| 85 | 6 | 50 | 33 |  |  | 59 | unless (( exists( $i18n_ref->{$entry} ) ) | 
| 86 |  |  |  |  |  |  | and ( defined( $i18n_ref->{$entry} ) ) ) | 
| 87 |  |  |  |  |  |  | { | 
| 88 | 0 |  |  |  |  | 0 | $stats->inc_missing; | 
| 89 | 0 |  |  |  |  | 0 | $warnings->add( 'missing', $entry ); | 
| 90 | 0 |  |  |  |  | 0 | next; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 6 | 50 |  |  |  | 14 | if ( $i18n_ref->{$entry} eq '' ) { | 
| 94 | 0 | 0 |  |  |  | 0 | unless ( has_empty($entry) ) { | 
| 95 | 0 |  |  |  |  | 0 | $stats->inc('empty'); | 
| 96 | 0 |  |  |  |  | 0 | $warnings->add( 'empty', $entry ); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  | else { | 
| 99 | 0 |  |  |  |  | 0 | $warnings->add( 'ignored', $entry ); | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | =head2 merge_data | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | Merges the translation data from a Jelly file and a Properties file. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | Expects as parameters: | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | =over | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | =item 1. | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | A hash reference with all the keys/values from a Jelly file. | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =item 2. | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | A hash reference with all the keys/values from a Properties file. | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | =back | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | This methods considers the way Jenkins is translated nowadays, considering | 
| 124 |  |  |  |  |  |  | different scenarios where the Jelly and Properties have different data. | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | Returns a hash reference with the keys and values merged. | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =cut | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub merge_data { | 
| 131 | 10 |  |  | 10 | 1 | 10155 | my ( $jelly_ref, $properties_ref ) = @_; | 
| 132 | 10 | 100 |  |  |  | 43 | confess('A hash reference of the Jelly keys is required') | 
| 133 |  |  |  |  |  |  | unless ($jelly_ref); | 
| 134 | 9 | 100 |  |  |  | 45 | confess('The Jelly type is invalid') unless ( ref($jelly_ref) eq 'HASH' ); | 
| 135 | 8 | 100 |  |  |  | 28 | confess('A hash reference of the Properties keys is required') | 
| 136 |  |  |  |  |  |  | unless ($properties_ref); | 
| 137 | 7 | 100 |  |  |  | 27 | confess('The Properties type is invalid') | 
| 138 |  |  |  |  |  |  | unless ( ref($properties_ref) eq 'HASH' ); | 
| 139 | 6 |  |  |  |  | 12 | my %merged; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 6 | 100 |  |  |  | 8 | if ( scalar( keys( %{$jelly_ref} ) ) == 0 ) { | 
|  | 6 |  |  |  |  | 22 |  | 
| 142 | 1 |  |  |  |  | 4 | return $properties_ref; | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 5 |  |  |  |  | 10 | foreach my $prop_key ( keys( %{$jelly_ref} ) ) { | 
|  | 5 |  |  |  |  | 15 |  | 
| 146 | 15 | 100 |  |  |  | 47 | if ( exists( $properties_ref->{$prop_key} ) ) { | 
| 147 | 7 |  |  |  |  | 15 | $merged{$prop_key} = $properties_ref->{$prop_key}; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | else { | 
| 150 | 8 |  |  |  |  | 20 | $merged{$prop_key} = $prop_key; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  | } | 
| 153 | 5 |  |  |  |  | 16 | return \%merged; | 
| 154 |  |  |  |  |  |  | } | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | =head2 dump_keys | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | Prints to C all keys from a hash, using some formatting to make it | 
| 159 |  |  |  |  |  |  | easier to read. | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | Expects as parameter a hash reference. | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | =cut | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub dump_keys { | 
| 166 | 6 |  |  | 6 | 1 | 9 | my $entries_ref = shift; | 
| 167 | 6 |  |  |  |  | 8 | foreach my $key ( keys( %{$entries_ref} ) ) { | 
|  | 6 |  |  |  |  | 29 |  | 
| 168 | 14 |  |  |  |  | 118 | print "\t$key\n"; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =head2 all_data | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | Retrieves all translation data from a single given file as reference. | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | Expects as parameter a complete path to a file. | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | This file can be a Properties or Jelly file. From that file name, it will be | 
| 179 |  |  |  |  |  |  | defined the related other files, by convention. | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Returns a array reference, where each index is: | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =over | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =item 1. | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | A hash reference with all keys/values for the English language. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =item 2. | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | A hash reference with all the keys/values for the related language. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =item 3. | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | A hash reference for the keys retrieved from the respective Jelly file. | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | =back | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | Any of the return references may point to an empty hash, but at list the first | 
| 200 |  |  |  |  |  |  | reference must point to a non-empty hash. | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =cut | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub all_data { | 
| 205 | 3 |  |  | 3 | 1 | 534 | my ( $file, $processor ) = @_; | 
| 206 | 3 | 100 |  |  |  | 14 | print "#####\nWorking on $file\n" if ( $processor->is_debug ); | 
| 207 | 3 |  |  |  |  | 18 | my ( $curr_lang_file, $english_file, $jelly_file ) | 
| 208 |  |  |  |  |  |  | = $processor->define_files($file); | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 3 | 100 |  |  |  | 13 | if ( $processor->is_debug ) { | 
| 211 | 2 |  |  |  |  | 23 | print "For file $file:\n", | 
| 212 |  |  |  |  |  |  | "\tthe localization file is $curr_lang_file\n", | 
| 213 |  |  |  |  |  |  | "\tand the source is $english_file\n"; | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | # entries_ref -> keys used in jelly or Message.properties files | 
| 217 |  |  |  |  |  |  | # lang_entries_ref -> keys/values in the desired language which are already | 
| 218 |  |  |  |  |  |  | # present in the file | 
| 219 | 3 |  |  |  |  | 10 | my ( $jelly_entries_ref, $lang_entries_ref, $english_entries_ref ); | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 3 | 50 |  |  |  | 58 | if ( -f $jelly_file ) { | 
| 222 | 3 |  |  |  |  | 15 | $jelly_entries_ref = load_jelly($jelly_file); | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  | else { | 
| 225 | 0 |  |  |  |  | 0 | $jelly_entries_ref = {}; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 | 3 |  |  |  |  | 20 | $english_entries_ref | 
| 229 |  |  |  |  |  |  | = load_properties( $english_file, $processor->is_debug ); | 
| 230 | 3 |  |  |  |  | 87 | $lang_entries_ref | 
| 231 |  |  |  |  |  |  | = load_properties( $curr_lang_file, $processor->is_debug ); | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 3 | 100 |  |  |  | 73 | if ( $processor->is_debug ) { | 
| 234 | 2 |  |  |  |  | 69 | print "All keys retrieved from $jelly_file:\n"; | 
| 235 | 2 |  |  |  |  | 10 | dump_keys($jelly_entries_ref); | 
| 236 | 2 |  |  |  |  | 17 | print "All keys retrieved from $english_file:\n"; | 
| 237 | 2 |  |  |  |  | 9 | dump_keys($english_entries_ref); | 
| 238 | 2 |  |  |  |  | 15 | print "All keys retrieved from $curr_lang_file:\n"; | 
| 239 | 2 |  |  |  |  | 6 | dump_keys($lang_entries_ref); | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 3 |  |  |  |  | 17 | return ( $jelly_entries_ref, $lang_entries_ref, $english_entries_ref ); | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | =head2 remove_unused | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | Remove unused keys from a properties file. | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | Each translation in every language depends on the original properties files | 
| 250 |  |  |  |  |  |  | that are written in English. | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | This function gets a set of keys and compare with those that are stored in the | 
| 253 |  |  |  |  |  |  | translation file: anything that exists outside the original set in English is | 
| 254 |  |  |  |  |  |  | considered deprecated and so removed. | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | Expects as positional parameters: | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | =over | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =item 1. | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | file: the complete path to the translation file to be checked. | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =item 2. | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | keys: a L instance of the keys from the original English properties | 
| 267 |  |  |  |  |  |  | file. | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =item 3. | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | license: a scalar reference with a license to include the header of the | 
| 272 |  |  |  |  |  |  | translated properties file. | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | =item 4 | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | backup: a boolean (0 or 1) if a backup file should be created in the same path | 
| 277 |  |  |  |  |  |  | of the file parameter. Optional. | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | =back | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | Returns the number of keys removed (as an integer). | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | =cut | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | sub remove_unused { | 
| 286 | 6 |  |  | 6 | 1 | 7786 | my $file = shift; | 
| 287 | 6 | 100 |  |  |  | 32 | confess "file is a required parameter\n" unless ( defined($file) ); | 
| 288 | 5 |  |  |  |  | 8 | my $keys = shift; | 
| 289 | 5 | 100 |  |  |  | 20 | confess "keys is a required parameter\n" unless ( defined($keys) ); | 
| 290 | 4 | 100 |  |  |  | 22 | confess "keys must be a Set::Tiny instance\n" | 
| 291 |  |  |  |  |  |  | unless ( ref($keys) eq 'Set::Tiny' ); | 
| 292 | 3 |  |  |  |  | 5 | my $license_ref = shift; | 
| 293 | 3 | 100 |  |  |  | 21 | confess "license must be an array reference" | 
| 294 |  |  |  |  |  |  | unless ( ref($license_ref) eq 'ARRAY' ); | 
| 295 | 2 |  |  |  |  | 3 | my $use_backup = shift; | 
| 296 | 2 | 100 |  |  |  | 6 | $use_backup = 0 unless ( defined($use_backup) ); | 
| 297 |  |  |  |  |  |  |  | 
| 298 | 2 |  |  |  |  | 4 | my $props_handler; | 
| 299 |  |  |  |  |  |  |  | 
| 300 | 2 | 100 |  |  |  | 6 | if ($use_backup) { | 
| 301 | 1 |  |  |  |  | 4 | my $backup = "$file.bak"; | 
| 302 | 1 | 50 |  |  |  | 59 | rename( $file, $backup ) | 
| 303 |  |  |  |  |  |  | or confess "Cannot rename $file to $backup: $!\n"; | 
| 304 | 1 |  |  |  |  | 9 | $props_handler = Jenkins::i18n::Properties->new( file => $backup ); | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  | else { | 
| 307 | 1 |  |  |  |  | 8 | $props_handler = Jenkins::i18n::Properties->new( file => $file ); | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 2 |  |  |  |  | 57 | my $curr_keys = Set::Tiny->new( $props_handler->propertyNames ); | 
| 311 | 2 |  |  |  |  | 244 | my $to_delete = $curr_keys->difference($keys); | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 2 |  |  |  |  | 70 | foreach my $key ( $to_delete->members ) { | 
| 314 | 24 |  |  |  |  | 319 | $props_handler->deleteProperty($key); | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 2 | 50 |  |  |  | 197 | open( my $out, '>', $file ) or confess "Cannot write to $file: $!\n"; | 
| 318 | 2 |  |  |  |  | 14 | $props_handler->save( $out, $license_ref ); | 
| 319 | 2 | 50 |  |  |  | 151 | close($out) or confess "Cannot save $file: $!\n"; | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 2 |  |  |  |  | 13 | return $to_delete->size; | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | =head2 find_files | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | Find all Jelly and Java Properties files that could be translated from English, | 
| 327 |  |  |  |  |  |  | i.e., files that do not have a ISO 639-1 standard language based code as a | 
| 328 |  |  |  |  |  |  | filename prefix (before the file extension). | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | Expects as parameters: | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | =over | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | =item 1. | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | The complete path to a directory that might contain such files. | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | =item 2. | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | An instance of L with all the languages codes identified. See | 
| 341 |  |  |  |  |  |  | C. | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | =back | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | Returns an L instance. | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | =cut | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | # Relative paths inside the Jenkins project repository | 
| 350 |  |  |  |  |  |  | my $src_test_path    = File::Spec->catfile( 'src',    'test' ); | 
| 351 |  |  |  |  |  |  | my $target_path      = File::Spec->catfile( 'target', '' ); | 
| 352 |  |  |  |  |  |  | my $src_regex        = qr/$src_test_path/; | 
| 353 |  |  |  |  |  |  | my $target_regex     = qr/$target_path/; | 
| 354 |  |  |  |  |  |  | my $msgs_regex       = qr/Messages\.properties$/; | 
| 355 |  |  |  |  |  |  | my $jelly_regex      = qr/\.jelly$/; | 
| 356 |  |  |  |  |  |  | my $properties_regex = qr/\.properties$/; | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | sub find_files { | 
| 359 | 5 |  |  | 5 | 1 | 4836 | my ( $dir, $all_known_langs ) = @_; | 
| 360 | 5 | 100 |  |  |  | 31 | confess 'Must provide a string, invalid directory parameter' | 
| 361 |  |  |  |  |  |  | unless ($dir); | 
| 362 | 4 | 100 |  |  |  | 21 | confess 'Must provide a string as directory, not a reference' | 
| 363 |  |  |  |  |  |  | unless ( ref($dir) eq '' ); | 
| 364 | 3 | 100 |  |  |  | 105 | confess "Directory '$dir' must exist" unless ( -d $dir ); | 
| 365 | 2 | 50 |  |  |  | 11 | confess "Must receive a Set::Tiny instance for langs parameter" | 
| 366 |  |  |  |  |  |  | unless ( ref($all_known_langs) eq 'Set::Tiny' ); | 
| 367 |  |  |  |  |  |  |  | 
| 368 | 2 |  |  |  |  | 4 | my $country_code_length = 2; | 
| 369 | 2 |  |  |  |  | 4 | my $lang_code_length    = 2; | 
| 370 | 2 |  |  |  |  | 4 | my $min_file_pieces     = 2; | 
| 371 | 2 |  |  |  |  | 10 | my $under_regex         = qr/_/; | 
| 372 | 2 |  |  |  |  | 15 | my $result              = Jenkins::i18n::FindResults->new; | 
| 373 | 2 |  |  |  |  | 15 | $result->add_warning( | 
| 374 |  |  |  |  |  |  | "Warning: ignoring the files at $src_test_path and $target_path paths." | 
| 375 |  |  |  |  |  |  | ); | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | find( | 
| 378 |  |  |  |  |  |  | sub { | 
| 379 | 13 |  |  | 13 |  | 33 | my $file = $File::Find::name; | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 13 | 50 | 33 |  |  | 98 | unless ( ( $file =~ $src_regex ) or ( $file =~ $target_regex ) ) { | 
| 382 | 13 | 100 | 100 |  |  | 78 | if (   ( $file =~ $msgs_regex ) | 
| 383 |  |  |  |  |  |  | or ( $file =~ $jelly_regex ) ) | 
| 384 |  |  |  |  |  |  | { | 
| 385 | 5 |  |  |  |  | 26 | $result->add_file($file); | 
| 386 |  |  |  |  |  |  | } | 
| 387 |  |  |  |  |  |  | else { | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 8 | 100 |  |  |  | 305 | if ( $file =~ $properties_regex ) { | 
| 390 | 5 |  |  |  |  | 71 | my $file_name = ( File::Spec->splitpath($file) )[-1]; | 
| 391 | 5 |  |  |  |  | 27 | $file_name =~ s/$properties_regex//; | 
| 392 | 5 |  |  |  |  | 23 | my @pieces = split( $under_regex, $file_name ); | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | # we must ignore a "_" at the beginning of the file | 
| 395 | 5 | 50 |  |  |  | 22 | shift @pieces if ( $pieces[0] eq '' ); | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 5 | 100 |  |  |  | 25 | if ( scalar(@pieces) < $min_file_pieces ) { | 
| 398 | 2 |  |  |  |  | 17 | $result->add_file($file); | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  | else { | 
| 401 | 3 | 50 | 33 |  |  | 38 | if ( | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 402 |  |  |  |  |  |  | ( scalar(@pieces) == $min_file_pieces ) | 
| 403 |  |  |  |  |  |  | and | 
| 404 |  |  |  |  |  |  | ( length( $pieces[-1] ) == $lang_code_length ) | 
| 405 |  |  |  |  |  |  | ) | 
| 406 |  |  |  |  |  |  | { | 
| 407 | 0 | 0 |  |  |  | 0 | $result->add_warning("Ignoring $file") | 
| 408 |  |  |  |  |  |  | if ( | 
| 409 |  |  |  |  |  |  | $all_known_langs->member( $pieces[-1] ) ); | 
| 410 |  |  |  |  |  |  | } | 
| 411 |  |  |  |  |  |  | elsif ( | 
| 412 |  |  |  |  |  |  | ( scalar(@pieces) > $min_file_pieces ) | 
| 413 |  |  |  |  |  |  | and ( | 
| 414 |  |  |  |  |  |  | length( $pieces[-1] ) | 
| 415 |  |  |  |  |  |  | == $country_code_length ) | 
| 416 |  |  |  |  |  |  | and | 
| 417 |  |  |  |  |  |  | ( length( $pieces[-2] ) == $lang_code_length ) | 
| 418 |  |  |  |  |  |  | ) | 
| 419 |  |  |  |  |  |  | { | 
| 420 | 3 | 50 |  |  |  | 18 | $result->add_warning("Ignoring $file") | 
| 421 |  |  |  |  |  |  | if ( | 
| 422 |  |  |  |  |  |  | $all_known_langs->member( | 
| 423 |  |  |  |  |  |  | $pieces[-2] . '_' . $pieces[-1] | 
| 424 |  |  |  |  |  |  | ) | 
| 425 |  |  |  |  |  |  | ); | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  | else { | 
| 428 | 0 |  |  |  |  | 0 | $result->add_file($file); | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  | } | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | }, | 
| 435 | 2 |  |  |  |  | 164 | $dir | 
| 436 |  |  |  |  |  |  | ); | 
| 437 | 2 |  |  |  |  | 65 | return $result; | 
| 438 |  |  |  |  |  |  | } | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | my $regex = qr/_([a-z]{2})(_[A-Z]{2})?\.properties$/; | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | =head2 find_langs | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | Finds all ISO 639-1 standard language based codes available in the Jenkins | 
| 445 |  |  |  |  |  |  | repository based on the filenames sufix (before the file extension) of the | 
| 446 |  |  |  |  |  |  | translated files. | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | This is basically the opposite of C does. | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | It expect as parameters the complete path to a directory to search for the | 
| 451 |  |  |  |  |  |  | files. | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | Returns a instance of the L class containing all the language codes | 
| 454 |  |  |  |  |  |  | that were identified. | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | Find all files Jelly and Java Properties files that could be translated from | 
| 457 |  |  |  |  |  |  | English, i.e., files that do not have a ISO 639-1 standard language based code | 
| 458 |  |  |  |  |  |  | as a filename prefix (before the file extension). | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | =cut | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | sub find_langs { | 
| 463 | 1 |  |  | 1 | 1 | 121 | my $dir = shift; | 
| 464 | 1 | 50 |  |  |  | 4 | confess 'Must provide a string, invalid directory parameter' | 
| 465 |  |  |  |  |  |  | unless ($dir); | 
| 466 | 1 | 50 |  |  |  | 48 | confess 'Must provide a string as directory, not a reference' | 
| 467 |  |  |  |  |  |  | unless ( ref($dir) eq '' ); | 
| 468 | 1 | 50 |  |  |  | 21 | confess "Directory '$dir' must exist" unless ( -d $dir ); | 
| 469 | 1 |  |  |  |  | 8 | my $langs = Set::Tiny->new; | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | find( | 
| 472 |  |  |  |  |  |  | sub { | 
| 473 | 4 |  |  | 4 |  | 12 | my $file = $File::Find::name; | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 4 | 50 | 33 |  |  | 33 | unless ( ( $file =~ $src_regex ) or ( $file =~ $target_regex ) ) { | 
| 476 | 4 | 100 |  |  |  | 123 | if ( $file =~ $regex ) { | 
| 477 | 1 |  |  |  |  | 3 | my $lang; | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 1 | 50 |  |  |  | 7 | if ($2) { | 
| 480 | 1 |  |  |  |  | 3 | $lang = $1 . $2; | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  | else { | 
| 483 | 0 |  |  |  |  | 0 | $lang = $1; | 
| 484 |  |  |  |  |  |  | } | 
| 485 |  |  |  |  |  |  |  | 
| 486 | 1 |  |  |  |  | 5 | $langs->insert($lang); | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  | } | 
| 489 |  |  |  |  |  |  | }, | 
| 490 | 1 |  |  |  |  | 112 | $dir | 
| 491 |  |  |  |  |  |  | ); | 
| 492 |  |  |  |  |  |  |  | 
| 493 | 1 |  |  |  |  | 57 | return $langs; | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | =head2 load_properties | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | Loads the content of a Java Properties file into a hash. | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | Expects as position parameters: | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | =over | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =item 1 | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | The complete path to a Java Properties file. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | =item 2 | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | True (1) or false (0) if a warn should be printed to C in case the file | 
| 511 |  |  |  |  |  |  | is missing. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | =back | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | Returns an hash reference with the file content. If the file doesn't exist, | 
| 516 |  |  |  |  |  |  | returns an empty hash reference. | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =cut | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | sub load_properties { | 
| 521 | 10 |  |  | 10 | 1 | 3140 | my ( $file, $must_warn ) = @_; | 
| 522 | 10 | 50 |  |  |  | 27 | confess 'The complete path to the properties file is required' | 
| 523 |  |  |  |  |  |  | unless ($file); | 
| 524 | 10 | 100 |  |  |  | 40 | confess 'Must pass if a warning is required or not' | 
| 525 |  |  |  |  |  |  | unless ( defined($must_warn) ); | 
| 526 |  |  |  |  |  |  |  | 
| 527 | 9 | 100 |  |  |  | 174 | unless ( -f $file ) { | 
| 528 | 2 | 100 |  |  |  | 19 | warn "File $file doesn't exist, skipping it...\n" if ($must_warn); | 
| 529 | 2 |  |  |  |  | 13 | return {}; | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 7 |  |  |  |  | 82 | my $props_handler = Jenkins::i18n::Properties->new( file => $file ); | 
| 533 | 7 |  |  |  |  | 214 | return $props_handler->getProperties; | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | =head2 load_jelly | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | Fill a hash with key/1 pairs from a C<.jelly> file. | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | Expects as parameter the path to a Jelly file. | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | Returns a hash reference. | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | =cut | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | # TODO: replace regex with XML parser | 
| 547 |  |  |  |  |  |  | sub load_jelly { | 
| 548 | 5 |  |  | 5 | 1 | 2338 | my $file = shift; | 
| 549 | 5 |  |  |  |  | 10 | my %ret; | 
| 550 |  |  |  |  |  |  |  | 
| 551 | 5 | 50 |  |  |  | 227 | open( my $fh, '<', $file ) or confess "Cannot read $file: $!\n"; | 
| 552 |  |  |  |  |  |  |  | 
| 553 | 5 |  |  |  |  | 152 | while (<$fh>) { | 
| 554 | 220 | 100 |  |  |  | 597 | next if ( !/\$\{.*?\%([^\(]+?).*\}/ ); | 
| 555 | 10 |  |  |  |  | 20 | my $line = $_; | 
| 556 | 10 |  | 66 |  |  | 56 | while ($line =~ /^.*?\$\{\%([^\(\}]+)(.*)$/ | 
| 557 |  |  |  |  |  |  | || $line =~ /^.*?\$\{.*?['"]\%([^\(\}\"\']+)(.*)$/ ) | 
| 558 |  |  |  |  |  |  | { | 
| 559 | 13 |  |  |  |  | 40 | $line = $2; | 
| 560 | 13 |  |  |  |  | 22 | my $word = $1; | 
| 561 | 13 |  |  |  |  | 26 | $word =~ s/\(.+$//g; | 
| 562 | 13 |  |  |  |  | 29 | $word =~ s/'+/''/g; | 
| 563 | 13 |  |  |  |  | 27 | $word =~ s/ /\\ /g; | 
| 564 | 13 |  |  |  |  | 31 | $word =~ s/\>/>/g; | 
| 565 | 13 |  |  |  |  | 57 | $word =~ s/\</ | 
| 566 | 13 |  |  |  |  | 23 | $word =~ s/\&/&/g; | 
| 567 | 13 |  |  |  |  | 25 | $word =~ s/([#:=])/\\$1/g; | 
| 568 | 13 |  |  |  |  | 105 | $ret{$word} = 1; | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  | } | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 5 |  |  |  |  | 58 | close($fh); | 
| 573 | 5 |  |  |  |  | 32 | return \%ret; | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | 1; | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | __END__ |