qa-check-lang.php 7.98 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	Description: Development tool to see which language phrases are missing or unused


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

Scott committed
22
define('QA_BASE_DIR', dirname(dirname(empty($_SERVER['SCRIPT_FILENAME']) ? __FILE__ : $_SERVER['SCRIPT_FILENAME'])) . '/');
Scott committed
23

Scott committed
24 25
require 'qa-base.php';
require_once QA_INCLUDE_DIR . 'app/users.php';
Scott committed
26

Scott committed
27 28
if (qa_get_logged_in_level() < QA_USER_LEVEL_ADMIN)
	qa_redirect('admin/general', null, qa_opt('site_url'));
Scott committed
29

Scott committed
30 31 32
header('Content-type: text/html; charset=utf-8');

?><!DOCTYPE html>
Scott committed
33 34 35 36 37
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
		<title>Question2Answer Language Check</title>
		<style>
38 39 40 41 42 43 44 45
			body {
				font-family: arial;
				font-size: 12px;
			}
			code {font-size: 125%;}
			.severe {color: #b83939;}
			.warning {color: #d2930d;}
			.includes {color: #999;}
Scott committed
46 47
		</style>
	</head>
48
	<body>
Scott committed
49 50
<?php

Scott committed
51 52 53
function get_phrase_substitutions($phrase)
{
	$substitutions = array();
Scott committed
54

55
	if (preg_match_all('/\^(([0-9]+)|([a-z_]+)|)/', $phrase, $matches)) {
Scott committed
56 57 58
		foreach ($matches[0] as $match) {
			@$substitutions[$match]++;
		}
59
	}
Scott committed
60

Scott committed
61 62
	return $substitutions;
}
Scott committed
63

Scott committed
64 65
echo '<code class="severe">Red = important to review.</code><br>';
echo '<code class="warning">Orange = probably safe to ignore.</code>';
Scott committed
66

Scott committed
67
echo '<h1>Checking US English files in <code>qa-include</code>...</h1>';
Scott committed
68

Scott committed
69
$includefiles = array_merge(glob(QA_INCLUDE_DIR . 'qa-*.php'), glob(QA_INCLUDE_DIR . '*/qa-*.php'), glob(QA_PLUGIN_DIR . '*/qa-*.php'));
Scott committed
70

Scott committed
71 72 73 74 75 76 77
$definite = array();
$probable = array();
$possible = array();
$defined = array();
$english = array();
$backmap = array();
$substitutions = array();
Scott committed
78

Scott committed
79
output_start_includes();
Scott committed
80

Scott committed
81 82
foreach ($includefiles as $includefile) {
	$contents = file_get_contents($includefile);
Scott committed
83

Scott committed
84
	preg_match_all('/qa_lang[a-z_]*\s*\(\s*[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
Scott committed
85

Scott committed
86 87 88 89 90
	foreach ($matches as $matchparts) {
		if ($matchparts[2] == 'date_month_') { // special case for month names
			for ($month = 1; $month <= 12; $month++) {
				@$definite[$matchparts[1]][$matchparts[2] . $month]++;
			}
Scott committed
91

Scott committed
92 93 94
		} else
			@$definite[$matchparts[1]][$matchparts[2]]++;
	}
Scott committed
95

Scott committed
96
	preg_match_all('/[\'\"]([a-z]+)\/([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
Scott committed
97

Scott committed
98 99 100
	foreach ($matches as $matchparts) {
		@$probable[$matchparts[1]][$matchparts[2]]++;
	}
Scott committed
101

Scott committed
102 103
	if (preg_match('|/qa-include/qa-lang-([a-z]+)\.php$|', $includefile, $matches)) { // it's a lang file
		$prefix = $matches[1];
Scott committed
104

Scott committed
105 106
		output_reading_include($includefile);
		$phrases = @include $includefile;
Scott committed
107

Scott committed
108 109 110 111 112 113
		foreach ($phrases as $key => $value) {
			@$defined[$prefix][$key]++;
			$english[$prefix][$key] = $value;
			$backmap[$value][] = array('prefix' => $prefix, 'key' => $key);
			$substitutions[$prefix][$key] = get_phrase_substitutions($value);
		}
Scott committed
114

Scott committed
115 116
	} else { // it's a different file
		preg_match_all('/[\'\"\/]([0-9a-z_]+)[\'\"]/', $contents, $matches, PREG_SET_ORDER);
Scott committed
117

Scott committed
118 119
		foreach ($matches as $matchparts) {
			@$possible[$matchparts[1]]++;
Scott committed
120 121
		}
	}
Scott committed
122
}
Scott committed
123

Scott committed
124
output_finish_includes();
Scott committed
125

Scott committed
126 127
foreach ($definite as $key => $valuecount) {
	foreach ($valuecount as $value => $count) {
128
		if (!@$defined[$key][$value]) {
Scott committed
129
			output_lang_issue($key, $value, 'used by ' . $count . ' file/s but not defined');
130
		}
Scott committed
131 132
	}
}
Scott committed
133

Scott committed
134 135
foreach ($defined as $key => $valuecount) {
	foreach ($valuecount as $value => $count) {
136
		if (!@$definite[$key][$value] && !@$probable[$key][$value] && !@$possible[$value]) {
Scott committed
137
			output_lang_issue($key, $value, 'defined but apparently not used');
138
		}
Scott committed
139 140
	}
}
Scott committed
141

Scott committed
142
foreach ($backmap as $phrase => $where) {
143
	if (count($where) > 1) {
Scott committed
144 145 146
		foreach ($where as $onewhere) {
			output_lang_issue($onewhere['prefix'], $onewhere['key'], 'contains the shared phrase "' . $phrase . '"', false);
		}
147
	}
Scott committed
148
}
Scott committed
149

Scott committed
150
require_once QA_INCLUDE_DIR . 'app/admin.php';
Scott committed
151

Scott committed
152 153
$languages = qa_admin_language_options();
unset($languages['']);
Scott committed
154

Scott committed
155 156
foreach ($languages as $code => $language) {
	echo '<h1>Checking ' . $language . ' files in <code>qa-lang/' . $code . '</code>...</h1>';
Scott committed
157

Scott committed
158 159 160 161 162
	$langdefined = array();
	$langdifferent = array();
	$langsubstitutions = array();
	$langincludefiles = glob(QA_LANG_DIR . $code . '/qa-*.php');
	$langnewphrases = array();
Scott committed
163

Scott committed
164
	output_start_includes();
Scott committed
165

Scott committed
166 167 168
	foreach ($langincludefiles as $langincludefile) {
		if (preg_match('/qa-lang-([a-z]+)\.php$/', $langincludefile, $matches)) { // it's a lang file
			$prefix = $matches[1];
Scott committed
169

Scott committed
170 171
			output_reading_include($langincludefile);
			$phrases = @include $langincludefile;
Scott committed
172

Scott committed
173 174 175 176
			foreach ($phrases as $key => $value) {
				@$langdefined[$prefix][$key]++;
				$langdifferent[$prefix][$key] = ($value != @$english[$prefix][$key]);
				$langsubstitutions[$prefix][$key] = get_phrase_substitutions($value);
Scott committed
177
			}
Scott committed
178 179
		}
	}
Scott committed
180

Scott committed
181
	output_finish_includes();
Scott committed
182

Scott committed
183 184
	foreach ($langdefined as $key => $valuecount) {
		foreach ($valuecount as $value => $count) {
185
			if (!@$defined[$key][$value]) {
Scott committed
186
				output_lang_issue($key, $value, 'defined but not in US English files');
Scott committed
187

188
			} elseif (!$langdifferent[$key][$value]) {
Scott committed
189
				output_lang_issue($key, $value, 'identical to US English files', false);
Scott committed
190

191
			} else {
Scott committed
192 193 194 195 196 197
				foreach ($substitutions[$key][$value] as $substitution => $subcount) {
					if (!@$langsubstitutions[$key][$value][$substitution])
						output_lang_issue($key, $value, 'omitted the substitution ' . $substitution);
					elseif ($subcount > @$langsubstitutions[$key][$value][$substitution])
						output_lang_issue($key, $value, 'has fewer of the substitution ' . $substitution);
				}
198
			}
Scott committed
199 200
		}
	}
Scott committed
201

Scott committed
202 203
	foreach ($defined as $key => $valuecount) {
		$showaserror = !(($key == 'admin') || ($key == 'options') || ($code == 'en-GB'));
Scott committed
204

Scott committed
205 206 207
		if (@$langdefined[$key]) {
			if (count($langdefined[$key]) < (count($valuecount) / 2)) { // only a few phrases defined
				output_lang_issue($key, null, 'few translations provided so will use US English defaults', $showaserror);
Scott committed
208

209
			} else {
Scott committed
210 211 212 213 214 215
				foreach ($valuecount as $value => $count) {
					if (!@$langdefined[$key][$value]) {
						output_lang_issue($key, $value, 'undefined so will use US English defaults', $showaserror);
						$langnewphrases[$key][$value] = $english[$key][$value];
					}
				}
216 217
			}
		} else {
Scott committed
218
			output_lang_issue($key, null, 'no translations provided so will use US English defaults', $showaserror);
219
		}
Scott committed
220
	}
Scott committed
221

Scott committed
222 223
	foreach ($langnewphrases as $prefix => $phrases) {
		echo '<h2>' . $language . ' phrases to add to <code>qa-lang/' . $code . '/qa-lang-' . $prefix . '.php</code>:</h2>';
Scott committed
224

Scott committed
225
		echo 'Copy and paste this into the middle of <code>qa-lang/' . $code . '/qa-lang-' . $prefix . '.php</code> then translate the right-hand side after the <code>=></code> symbol.';
Scott committed
226

Scott committed
227
		echo '<pre>';
Scott committed
228

Scott committed
229 230
		foreach ($phrases as $key => $value) {
			echo '<span style="font-size:25%;">' . "\t\t</span>'" . $key . "' => \"" . strtr($value, array('\\' => '\\\\', '"' => '\"', '$' => '\$', "\n" => '\n', "\t" => '\t')) . "\",\n";
Scott committed
231
		}
Scott committed
232 233

		echo '</pre>';
Scott committed
234
	}
Scott committed
235
}
Scott committed
236 237


Scott committed
238 239 240
function output_lang_issue($prefix, $key, $issue, $error = true)
{
	$colorClass = $error ? 'severe' : 'warning';
Scott committed
241

Scott committed
242
	$htmlKey = strlen($key) > 0 ? "'<strong>" . qa_html($key) . "</strong>'" : '';
Scott committed
243

Scott committed
244 245 246 247 248
	echo sprintf(
		'<code class="%s">qa-lang-<strong>%s</strong>.php:%s</code> &nbsp; %s<br>',
		$colorClass, qa_html($prefix), $htmlKey, qa_html($issue)
	);
}
Scott committed
249 250


Scott committed
251 252 253
function output_start_includes()
{
	global $oneread;
Scott committed
254

Scott committed
255
	$oneread = false;
Scott committed
256

Scott committed
257 258
	echo '<p class="includes">Reading: ';
}
Scott committed
259 260


Scott committed
261 262 263
function output_reading_include($file)
{
	global $oneread;
Scott committed
264

Scott committed
265 266
	echo ($oneread ? ', ' : '') . htmlspecialchars(basename($file));
	flush();
Scott committed
267

Scott committed
268 269
	$oneread = true;
}
Scott committed
270 271


Scott committed
272 273 274 275
function output_finish_includes()
{
	echo '</p>';
}
Scott committed
276 277


Scott committed
278
echo '<h1>Finished scanning for problems!</h1>';
Scott committed
279 280 281 282

?>

	</body>
Scott committed
283
</html>