aboutsummaryrefslogtreecommitdiff
path: root/cli/extract-js-localizations.php
blob: 70d14a9a0417245f762d0c941b3ef13ca8cbe8cc (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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#!/usr/bin/env php
<?php
/**
 * extract-js-localizations.php
 *
 * Exports all strings from js into app/views/localizations/show.php so
 * they can be translated as well.
 *
 * @author    Jan-Hendrik Willms <tleilax+studip@gmail.com>
 * @license   GPL2 or any later version
 * @copyright Stud.IP Core Group
 * @since     3.1
 */

require 'studip_cli_env.inc.php';

/**
 * Determines whether the file should be skipped depending on an exclude list
 * with an additional include list. This allows inclusion inside of previously
 * excluded entries. We need this for the assets directory.
 * Furthermore, the file is checked against a list of mime types to include.
 *
 * @param String $filename Adjusted filename (stripped to path inside trunk)
 * @param String $realfile Actual file name (needed for mime type detection)
 * @return bool indicating whether the file should be skipped or not
 */
function should_skip_file($filename, $realfile) {
    $exclude = [
        'cli/*',
        'composer/*',
        'config/*',
        'data/*',
        'db/*',
        'doc/*',
        'locale/*',
        'node_modules/*',
        'public/assets/flash*',
        'public/assets/fonts*',
        'public/assets/images*',
        'public/assets/javascripts/*',
        'public/assets/sounds*',
        'public/assets/squeezed*',
        'public/assets/stylesheets*',
        'public/pictures/*',
        'public/plugins_packages/*',
        'test/*',
        'tests/*',
        'vendor/*',
    ];
    $include = [
        'public/assets/javascripts/ckeditor*',
        'public/plugins_packages/core*',
    ];
    $mime_types = [
        'text/*',
        'application/javascript',
    ];

    // Check if the file should be excluded, depending on it's path.
    $matching_pattern = null;
    $skip             = false;
    foreach ($exclude as $pattern) {
        if (fnmatch($pattern, $filename)) {
            $matching_pattern = $pattern;
            $skip             = true;
            break;
        }
    }

    // If it should be skipped in step 1, check if it matches the include
    // patterns and no longer skip it, if it matches.
    // Matches are only from patterns that are longer than the pattern that
    // set the entry to be skipped. Thus it is detected if the file is in a
    // subdirectory.
    if ($skip) {
        foreach ($include as $pattern) {
            if (fnmatch($pattern, $filename) && mb_strlen($pattern) > mb_strlen($matching_pattern)) {
                $skip = false;
                break;
            }
        }
    }

    // If the file should not be skipped, check it's mime type and skip it
    // if the mime type is not allowed.
    if (!$skip && is_file($realfile)) {
        $mime_type = mime_content_type($realfile);

        $skip = true;
        foreach ($mime_types as $pattern) {
            if (fnmatch($pattern, $mime_type)) {
                $skip = false;
                break;
            }
        }
    }

    return $skip;
}

/**
 * Extract the actual text strings from a file. This will only detect single
 * line text strings. Multi line strings are just a hassle to handle in js
 * anyways.
 *
 * @param String $file Filename to extract text strings from
 * @return mixed Array with found text strings or false if no text strings
 *               were found
 */
function extract_strings($file) {
    $contents = file_get_contents($file);
    $regexp   = '/(?:\'([^\']+)\'|"([^"]+)")\\.toLocaleString\\(\\s*\\)/';

    if (preg_match_all($regexp, $contents, $matches, PREG_SET_ORDER)) {
        $result = [];
        foreach ($matches as $match) {
            $result[] = $match[1] ?: $match[2];
        }
        return array_unique($result);
    }

    return false;
}

/**
 * Recursively find text strings in files in the given directory.
 * This skips invalid files.
 *
 * @param String $directory Directory to search files in
 * @param mixed  $base      Optional base directory to strip from file names,
 *                          will default to the initial passed directory.
 * @return Array Associative array with filenames as index and an array of
 *               the text strings the file contains.
 */
function find_strings_in_dir($directory, $base = null) {
    $result = [];

    $base = rtrim($base ?: $directory, '/') . '/';

    $files = glob(rtrim($directory, '/') . '/*');
    foreach ($files as $file) {
        $filename = str_replace($base, '', $file);
        $is_dir   = is_dir($file);

        if (should_skip_file($filename, $file)) {
            continue;
        }

        if (is_dir($file)) {
            $result += find_strings_in_dir($file, $base);
        } elseif ($strings = extract_strings($file)) {
            $result[$filename] = $strings;
        }
    }

    return $result;
}

// Find text strings in all stud.ip files
$occurences = find_strings_in_dir(realpath(__DIR__ . '/..'));

// Remove duplicates
$hashes = [];
foreach ($occurences as $file => $strings) {
    foreach ($strings as $index => $string) {
        $hash = md5($string);
        if (in_array($hash, $hashes)) {
            unset($strings[$index]);
        } else {
            $hashes[] = $hash;
        }
    }
    if (empty($strings)) {
        unset($occurences[$file]);
    } else {
        $occurences[$file] = $strings;
    }
}

// Create trails view as output
ob_start();
?>
<?= '<?php' . PHP_EOL ?>

$translations = array(
<? foreach ($occurences as $file => $strings): ?>
    // <?= $file . PHP_EOL ?>
<? foreach ($strings as $string): ?>
    '<?= addcslashes($string, "'") ?>' => _('<?= addcslashes($string, "'") ?>'),
<? endforeach; ?>

<? endforeach; ?>
);

?>
<?= '<?=' ?> json_encode($translations) <?= '?>' ?>
<?
$view = ob_get_clean();

// Write output to the corresponding file
file_put_contents(__DIR__ . '/../app/views/localizations/show.php', $view);

// Show some statistics
printf('%u strings written to file' . PHP_EOL, array_sum(array_map('count', $occurences)));