Completed
Pull Request — master (#7351)
by Björn
15:10
created
lib/private/L10N/Factory.php 1 patch
Indentation   +400 added lines, -400 removed lines patch added patch discarded remove patch
@@ -40,404 +40,404 @@
 block discarded – undo
40 40
  */
41 41
 class Factory implements IFactory {
42 42
 
43
-	/** @var string */
44
-	protected $requestLanguage = '';
45
-
46
-	/**
47
-	 * cached instances
48
-	 * @var array Structure: Lang => App => \OCP\IL10N
49
-	 */
50
-	protected $instances = [];
51
-
52
-	/**
53
-	 * @var array Structure: App => string[]
54
-	 */
55
-	protected $availableLanguages = [];
56
-
57
-	/**
58
-	 * @var array Structure: string => callable
59
-	 */
60
-	protected $pluralFunctions = [];
61
-
62
-	/** @var IConfig */
63
-	protected $config;
64
-
65
-	/** @var IRequest */
66
-	protected $request;
67
-
68
-	/** @var IUserSession */
69
-	protected $userSession;
70
-
71
-	/** @var string */
72
-	protected $serverRoot;
73
-
74
-	/**
75
-	 * @param IConfig $config
76
-	 * @param IRequest $request
77
-	 * @param IUserSession $userSession
78
-	 * @param string $serverRoot
79
-	 */
80
-	public function __construct(IConfig $config,
81
-								IRequest $request,
82
-								IUserSession $userSession,
83
-								$serverRoot) {
84
-		$this->config = $config;
85
-		$this->request = $request;
86
-		$this->userSession = $userSession;
87
-		$this->serverRoot = $serverRoot;
88
-	}
89
-
90
-	/**
91
-	 * Get a language instance
92
-	 *
93
-	 * @param string $app
94
-	 * @param string|null $lang
95
-	 * @return \OCP\IL10N
96
-	 */
97
-	public function get($app, $lang = null) {
98
-		$app = \OC_App::cleanAppId($app);
99
-		if ($lang !== null) {
100
-			$lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang);
101
-		}
102
-
103
-		$forceLang = $this->config->getSystemValue('force_language', false);
104
-		if (is_string($forceLang)) {
105
-			$lang = $forceLang;
106
-		}
107
-
108
-		if ($lang === null || !$this->languageExists($app, $lang)) {
109
-			$lang = $this->findLanguage($app);
110
-		}
111
-
112
-		if (!isset($this->instances[$lang][$app])) {
113
-			$this->instances[$lang][$app] = new L10N(
114
-				$this, $app, $lang,
115
-				$this->getL10nFilesForApp($app, $lang)
116
-			);
117
-		}
118
-
119
-		return $this->instances[$lang][$app];
120
-	}
121
-
122
-	/**
123
-	 * Find the best language
124
-	 *
125
-	 * @param string|null $app App id or null for core
126
-	 * @return string language If nothing works it returns 'en'
127
-	 */
128
-	public function findLanguage($app = null) {
129
-		if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
130
-			return $this->requestLanguage;
131
-		}
132
-
133
-		/**
134
-		 * At this point Nextcloud might not yet be installed and thus the lookup
135
-		 * in the preferences table might fail. For this reason we need to check
136
-		 * whether the instance has already been installed
137
-		 *
138
-		 * @link https://github.com/owncloud/core/issues/21955
139
-		 */
140
-		if($this->config->getSystemValue('installed', false)) {
141
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
142
-			if(!is_null($userId)) {
143
-				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
144
-			} else {
145
-				$userLang = null;
146
-			}
147
-		} else {
148
-			$userId = null;
149
-			$userLang = null;
150
-		}
151
-
152
-		if ($userLang) {
153
-			$this->requestLanguage = $userLang;
154
-			if ($this->languageExists($app, $userLang)) {
155
-				return $userLang;
156
-			}
157
-		}
158
-
159
-		try {
160
-			// Try to get the language from the Request
161
-			$lang = $this->getLanguageFromRequest($app);
162
-			if ($userId !== null && $app === null && !$userLang) {
163
-				$this->config->setUserValue($userId, 'core', 'lang', $lang);
164
-			}
165
-			return $lang;
166
-		} catch (LanguageNotFoundException $e) {
167
-			// Finding language from request failed fall back to default language
168
-			$defaultLanguage = $this->config->getSystemValue('default_language', false);
169
-			if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
170
-				return $defaultLanguage;
171
-			}
172
-		}
173
-
174
-		// We could not find any language so fall back to english
175
-		return 'en';
176
-	}
177
-
178
-	/**
179
-	 * Find all available languages for an app
180
-	 *
181
-	 * @param string|null $app App id or null for core
182
-	 * @return array an array of available languages
183
-	 */
184
-	public function findAvailableLanguages($app = null) {
185
-		$key = $app;
186
-		if ($key === null) {
187
-			$key = 'null';
188
-		}
189
-
190
-		// also works with null as key
191
-		if (!empty($this->availableLanguages[$key])) {
192
-			return $this->availableLanguages[$key];
193
-		}
194
-
195
-		$available = ['en']; //english is always available
196
-		$dir = $this->findL10nDir($app);
197
-		if (is_dir($dir)) {
198
-			$files = scandir($dir);
199
-			if ($files !== false) {
200
-				foreach ($files as $file) {
201
-					if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
202
-						$available[] = substr($file, 0, -5);
203
-					}
204
-				}
205
-			}
206
-		}
207
-
208
-		// merge with translations from theme
209
-		$theme = $this->config->getSystemValue('theme');
210
-		if (!empty($theme)) {
211
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
212
-
213
-			if (is_dir($themeDir)) {
214
-				$files = scandir($themeDir);
215
-				if ($files !== false) {
216
-					foreach ($files as $file) {
217
-						if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
218
-							$available[] = substr($file, 0, -5);
219
-						}
220
-					}
221
-				}
222
-			}
223
-		}
224
-
225
-		$this->availableLanguages[$key] = $available;
226
-		return $available;
227
-	}
228
-
229
-	/**
230
-	 * @param string|null $app App id or null for core
231
-	 * @param string $lang
232
-	 * @return bool
233
-	 */
234
-	public function languageExists($app, $lang) {
235
-		if ($lang === 'en') {//english is always available
236
-			return true;
237
-		}
238
-
239
-		$languages = $this->findAvailableLanguages($app);
240
-		return array_search($lang, $languages) !== false;
241
-	}
242
-
243
-	/**
244
-	 * @param string|null $app
245
-	 * @return string
246
-	 * @throws LanguageNotFoundException
247
-	 */
248
-	private function getLanguageFromRequest($app) {
249
-		$header = $this->request->getHeader('ACCEPT_LANGUAGE');
250
-		if ($header) {
251
-			$available = $this->findAvailableLanguages($app);
252
-
253
-			// E.g. make sure that 'de' is before 'de_DE'.
254
-			sort($available);
255
-
256
-			$preferences = preg_split('/,\s*/', strtolower($header));
257
-			foreach ($preferences as $preference) {
258
-				list($preferred_language) = explode(';', $preference);
259
-				$preferred_language = str_replace('-', '_', $preferred_language);
260
-
261
-				foreach ($available as $available_language) {
262
-					if ($preferred_language === strtolower($available_language)) {
263
-						return $this->respectDefaultLanguage($app, $available_language);
264
-					}
265
-				}
266
-
267
-				// Fallback from de_De to de
268
-				foreach ($available as $available_language) {
269
-					if (substr($preferred_language, 0, 2) === $available_language) {
270
-						return $available_language;
271
-					}
272
-				}
273
-			}
274
-		}
275
-
276
-		throw new LanguageNotFoundException();
277
-	}
278
-
279
-	/**
280
-	 * if default language is set to de_DE (formal German) this should be
281
-	 * preferred to 'de' (non-formal German) if possible
282
-	 *
283
-	 * @param string|null $app
284
-	 * @param string $lang
285
-	 * @return string
286
-	 */
287
-	protected function respectDefaultLanguage($app, $lang) {
288
-		$result = $lang;
289
-		$defaultLanguage = $this->config->getSystemValue('default_language', false);
290
-
291
-		// use formal version of german ("Sie" instead of "Du") if the default
292
-		// language is set to 'de_DE' if possible
293
-		if (is_string($defaultLanguage) &&
294
-			strtolower($lang) === 'de' &&
295
-			strtolower($defaultLanguage) === 'de_de' &&
296
-			$this->languageExists($app, 'de_DE')
297
-		) {
298
-			$result = 'de_DE';
299
-		}
300
-
301
-		return $result;
302
-	}
303
-
304
-	/**
305
-	 * Checks if $sub is a subdirectory of $parent
306
-	 *
307
-	 * @param string $sub
308
-	 * @param string $parent
309
-	 * @return bool
310
-	 */
311
-	private function isSubDirectory($sub, $parent) {
312
-		// Check whether $sub contains no ".."
313
-		if(strpos($sub, '..') !== false) {
314
-			return false;
315
-		}
316
-
317
-		// Check whether $sub is a subdirectory of $parent
318
-		if (strpos($sub, $parent) === 0) {
319
-			return true;
320
-		}
321
-
322
-		return false;
323
-	}
324
-
325
-	/**
326
-	 * Get a list of language files that should be loaded
327
-	 *
328
-	 * @param string $app
329
-	 * @param string $lang
330
-	 * @return string[]
331
-	 */
332
-	// FIXME This method is only public, until OC_L10N does not need it anymore,
333
-	// FIXME This is also the reason, why it is not in the public interface
334
-	public function getL10nFilesForApp($app, $lang) {
335
-		$languageFiles = [];
336
-
337
-		$i18nDir = $this->findL10nDir($app);
338
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
339
-
340
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
341
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
342
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/')
343
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
344
-			)
345
-			&& file_exists($transFile)) {
346
-			// load the translations file
347
-			$languageFiles[] = $transFile;
348
-		}
349
-
350
-		// merge with translations from theme
351
-		$theme = $this->config->getSystemValue('theme');
352
-		if (!empty($theme)) {
353
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
354
-			if (file_exists($transFile)) {
355
-				$languageFiles[] = $transFile;
356
-			}
357
-		}
358
-
359
-		return $languageFiles;
360
-	}
361
-
362
-	/**
363
-	 * find the l10n directory
364
-	 *
365
-	 * @param string $app App id or empty string for core
366
-	 * @return string directory
367
-	 */
368
-	protected function findL10nDir($app = null) {
369
-		if (in_array($app, ['core', 'lib', 'settings'])) {
370
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
371
-				return $this->serverRoot . '/' . $app . '/l10n/';
372
-			}
373
-		} else if ($app && \OC_App::getAppPath($app) !== false) {
374
-			// Check if the app is in the app folder
375
-			return \OC_App::getAppPath($app) . '/l10n/';
376
-		}
377
-		return $this->serverRoot . '/core/l10n/';
378
-	}
379
-
380
-
381
-	/**
382
-	 * Creates a function from the plural string
383
-	 *
384
-	 * Parts of the code is copied from Habari:
385
-	 * https://github.com/habari/system/blob/master/classes/locale.php
386
-	 * @param string $string
387
-	 * @return string
388
-	 */
389
-	public function createPluralFunction($string) {
390
-		if (isset($this->pluralFunctions[$string])) {
391
-			return $this->pluralFunctions[$string];
392
-		}
393
-
394
-		if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
395
-			// sanitize
396
-			$nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
397
-			$plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
398
-
399
-			$body = str_replace(
400
-				array( 'plural', 'n', '$n$plurals', ),
401
-				array( '$plural', '$n', '$nplurals', ),
402
-				'nplurals='. $nplurals . '; plural=' . $plural
403
-			);
404
-
405
-			// add parents
406
-			// important since PHP's ternary evaluates from left to right
407
-			$body .= ';';
408
-			$res = '';
409
-			$p = 0;
410
-			for($i = 0; $i < strlen($body); $i++) {
411
-				$ch = $body[$i];
412
-				switch ( $ch ) {
413
-					case '?':
414
-						$res .= ' ? (';
415
-						$p++;
416
-						break;
417
-					case ':':
418
-						$res .= ') : (';
419
-						break;
420
-					case ';':
421
-						$res .= str_repeat( ')', $p ) . ';';
422
-						$p = 0;
423
-						break;
424
-					default:
425
-						$res .= $ch;
426
-				}
427
-			}
428
-
429
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
430
-			$function = create_function('$n', $body);
431
-			$this->pluralFunctions[$string] = $function;
432
-			return $function;
433
-		} else {
434
-			// default: one plural form for all cases but n==1 (english)
435
-			$function = create_function(
436
-				'$n',
437
-				'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
438
-			);
439
-			$this->pluralFunctions[$string] = $function;
440
-			return $function;
441
-		}
442
-	}
43
+    /** @var string */
44
+    protected $requestLanguage = '';
45
+
46
+    /**
47
+     * cached instances
48
+     * @var array Structure: Lang => App => \OCP\IL10N
49
+     */
50
+    protected $instances = [];
51
+
52
+    /**
53
+     * @var array Structure: App => string[]
54
+     */
55
+    protected $availableLanguages = [];
56
+
57
+    /**
58
+     * @var array Structure: string => callable
59
+     */
60
+    protected $pluralFunctions = [];
61
+
62
+    /** @var IConfig */
63
+    protected $config;
64
+
65
+    /** @var IRequest */
66
+    protected $request;
67
+
68
+    /** @var IUserSession */
69
+    protected $userSession;
70
+
71
+    /** @var string */
72
+    protected $serverRoot;
73
+
74
+    /**
75
+     * @param IConfig $config
76
+     * @param IRequest $request
77
+     * @param IUserSession $userSession
78
+     * @param string $serverRoot
79
+     */
80
+    public function __construct(IConfig $config,
81
+                                IRequest $request,
82
+                                IUserSession $userSession,
83
+                                $serverRoot) {
84
+        $this->config = $config;
85
+        $this->request = $request;
86
+        $this->userSession = $userSession;
87
+        $this->serverRoot = $serverRoot;
88
+    }
89
+
90
+    /**
91
+     * Get a language instance
92
+     *
93
+     * @param string $app
94
+     * @param string|null $lang
95
+     * @return \OCP\IL10N
96
+     */
97
+    public function get($app, $lang = null) {
98
+        $app = \OC_App::cleanAppId($app);
99
+        if ($lang !== null) {
100
+            $lang = str_replace(array('\0', '/', '\\', '..'), '', (string) $lang);
101
+        }
102
+
103
+        $forceLang = $this->config->getSystemValue('force_language', false);
104
+        if (is_string($forceLang)) {
105
+            $lang = $forceLang;
106
+        }
107
+
108
+        if ($lang === null || !$this->languageExists($app, $lang)) {
109
+            $lang = $this->findLanguage($app);
110
+        }
111
+
112
+        if (!isset($this->instances[$lang][$app])) {
113
+            $this->instances[$lang][$app] = new L10N(
114
+                $this, $app, $lang,
115
+                $this->getL10nFilesForApp($app, $lang)
116
+            );
117
+        }
118
+
119
+        return $this->instances[$lang][$app];
120
+    }
121
+
122
+    /**
123
+     * Find the best language
124
+     *
125
+     * @param string|null $app App id or null for core
126
+     * @return string language If nothing works it returns 'en'
127
+     */
128
+    public function findLanguage($app = null) {
129
+        if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
130
+            return $this->requestLanguage;
131
+        }
132
+
133
+        /**
134
+         * At this point Nextcloud might not yet be installed and thus the lookup
135
+         * in the preferences table might fail. For this reason we need to check
136
+         * whether the instance has already been installed
137
+         *
138
+         * @link https://github.com/owncloud/core/issues/21955
139
+         */
140
+        if($this->config->getSystemValue('installed', false)) {
141
+            $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
142
+            if(!is_null($userId)) {
143
+                $userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
144
+            } else {
145
+                $userLang = null;
146
+            }
147
+        } else {
148
+            $userId = null;
149
+            $userLang = null;
150
+        }
151
+
152
+        if ($userLang) {
153
+            $this->requestLanguage = $userLang;
154
+            if ($this->languageExists($app, $userLang)) {
155
+                return $userLang;
156
+            }
157
+        }
158
+
159
+        try {
160
+            // Try to get the language from the Request
161
+            $lang = $this->getLanguageFromRequest($app);
162
+            if ($userId !== null && $app === null && !$userLang) {
163
+                $this->config->setUserValue($userId, 'core', 'lang', $lang);
164
+            }
165
+            return $lang;
166
+        } catch (LanguageNotFoundException $e) {
167
+            // Finding language from request failed fall back to default language
168
+            $defaultLanguage = $this->config->getSystemValue('default_language', false);
169
+            if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
170
+                return $defaultLanguage;
171
+            }
172
+        }
173
+
174
+        // We could not find any language so fall back to english
175
+        return 'en';
176
+    }
177
+
178
+    /**
179
+     * Find all available languages for an app
180
+     *
181
+     * @param string|null $app App id or null for core
182
+     * @return array an array of available languages
183
+     */
184
+    public function findAvailableLanguages($app = null) {
185
+        $key = $app;
186
+        if ($key === null) {
187
+            $key = 'null';
188
+        }
189
+
190
+        // also works with null as key
191
+        if (!empty($this->availableLanguages[$key])) {
192
+            return $this->availableLanguages[$key];
193
+        }
194
+
195
+        $available = ['en']; //english is always available
196
+        $dir = $this->findL10nDir($app);
197
+        if (is_dir($dir)) {
198
+            $files = scandir($dir);
199
+            if ($files !== false) {
200
+                foreach ($files as $file) {
201
+                    if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
202
+                        $available[] = substr($file, 0, -5);
203
+                    }
204
+                }
205
+            }
206
+        }
207
+
208
+        // merge with translations from theme
209
+        $theme = $this->config->getSystemValue('theme');
210
+        if (!empty($theme)) {
211
+            $themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
212
+
213
+            if (is_dir($themeDir)) {
214
+                $files = scandir($themeDir);
215
+                if ($files !== false) {
216
+                    foreach ($files as $file) {
217
+                        if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
218
+                            $available[] = substr($file, 0, -5);
219
+                        }
220
+                    }
221
+                }
222
+            }
223
+        }
224
+
225
+        $this->availableLanguages[$key] = $available;
226
+        return $available;
227
+    }
228
+
229
+    /**
230
+     * @param string|null $app App id or null for core
231
+     * @param string $lang
232
+     * @return bool
233
+     */
234
+    public function languageExists($app, $lang) {
235
+        if ($lang === 'en') {//english is always available
236
+            return true;
237
+        }
238
+
239
+        $languages = $this->findAvailableLanguages($app);
240
+        return array_search($lang, $languages) !== false;
241
+    }
242
+
243
+    /**
244
+     * @param string|null $app
245
+     * @return string
246
+     * @throws LanguageNotFoundException
247
+     */
248
+    private function getLanguageFromRequest($app) {
249
+        $header = $this->request->getHeader('ACCEPT_LANGUAGE');
250
+        if ($header) {
251
+            $available = $this->findAvailableLanguages($app);
252
+
253
+            // E.g. make sure that 'de' is before 'de_DE'.
254
+            sort($available);
255
+
256
+            $preferences = preg_split('/,\s*/', strtolower($header));
257
+            foreach ($preferences as $preference) {
258
+                list($preferred_language) = explode(';', $preference);
259
+                $preferred_language = str_replace('-', '_', $preferred_language);
260
+
261
+                foreach ($available as $available_language) {
262
+                    if ($preferred_language === strtolower($available_language)) {
263
+                        return $this->respectDefaultLanguage($app, $available_language);
264
+                    }
265
+                }
266
+
267
+                // Fallback from de_De to de
268
+                foreach ($available as $available_language) {
269
+                    if (substr($preferred_language, 0, 2) === $available_language) {
270
+                        return $available_language;
271
+                    }
272
+                }
273
+            }
274
+        }
275
+
276
+        throw new LanguageNotFoundException();
277
+    }
278
+
279
+    /**
280
+     * if default language is set to de_DE (formal German) this should be
281
+     * preferred to 'de' (non-formal German) if possible
282
+     *
283
+     * @param string|null $app
284
+     * @param string $lang
285
+     * @return string
286
+     */
287
+    protected function respectDefaultLanguage($app, $lang) {
288
+        $result = $lang;
289
+        $defaultLanguage = $this->config->getSystemValue('default_language', false);
290
+
291
+        // use formal version of german ("Sie" instead of "Du") if the default
292
+        // language is set to 'de_DE' if possible
293
+        if (is_string($defaultLanguage) &&
294
+            strtolower($lang) === 'de' &&
295
+            strtolower($defaultLanguage) === 'de_de' &&
296
+            $this->languageExists($app, 'de_DE')
297
+        ) {
298
+            $result = 'de_DE';
299
+        }
300
+
301
+        return $result;
302
+    }
303
+
304
+    /**
305
+     * Checks if $sub is a subdirectory of $parent
306
+     *
307
+     * @param string $sub
308
+     * @param string $parent
309
+     * @return bool
310
+     */
311
+    private function isSubDirectory($sub, $parent) {
312
+        // Check whether $sub contains no ".."
313
+        if(strpos($sub, '..') !== false) {
314
+            return false;
315
+        }
316
+
317
+        // Check whether $sub is a subdirectory of $parent
318
+        if (strpos($sub, $parent) === 0) {
319
+            return true;
320
+        }
321
+
322
+        return false;
323
+    }
324
+
325
+    /**
326
+     * Get a list of language files that should be loaded
327
+     *
328
+     * @param string $app
329
+     * @param string $lang
330
+     * @return string[]
331
+     */
332
+    // FIXME This method is only public, until OC_L10N does not need it anymore,
333
+    // FIXME This is also the reason, why it is not in the public interface
334
+    public function getL10nFilesForApp($app, $lang) {
335
+        $languageFiles = [];
336
+
337
+        $i18nDir = $this->findL10nDir($app);
338
+        $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
339
+
340
+        if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
341
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
342
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/')
343
+                || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
344
+            )
345
+            && file_exists($transFile)) {
346
+            // load the translations file
347
+            $languageFiles[] = $transFile;
348
+        }
349
+
350
+        // merge with translations from theme
351
+        $theme = $this->config->getSystemValue('theme');
352
+        if (!empty($theme)) {
353
+            $transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
354
+            if (file_exists($transFile)) {
355
+                $languageFiles[] = $transFile;
356
+            }
357
+        }
358
+
359
+        return $languageFiles;
360
+    }
361
+
362
+    /**
363
+     * find the l10n directory
364
+     *
365
+     * @param string $app App id or empty string for core
366
+     * @return string directory
367
+     */
368
+    protected function findL10nDir($app = null) {
369
+        if (in_array($app, ['core', 'lib', 'settings'])) {
370
+            if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
371
+                return $this->serverRoot . '/' . $app . '/l10n/';
372
+            }
373
+        } else if ($app && \OC_App::getAppPath($app) !== false) {
374
+            // Check if the app is in the app folder
375
+            return \OC_App::getAppPath($app) . '/l10n/';
376
+        }
377
+        return $this->serverRoot . '/core/l10n/';
378
+    }
379
+
380
+
381
+    /**
382
+     * Creates a function from the plural string
383
+     *
384
+     * Parts of the code is copied from Habari:
385
+     * https://github.com/habari/system/blob/master/classes/locale.php
386
+     * @param string $string
387
+     * @return string
388
+     */
389
+    public function createPluralFunction($string) {
390
+        if (isset($this->pluralFunctions[$string])) {
391
+            return $this->pluralFunctions[$string];
392
+        }
393
+
394
+        if (preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
395
+            // sanitize
396
+            $nplurals = preg_replace( '/[^0-9]/', '', $matches[1] );
397
+            $plural = preg_replace( '#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2] );
398
+
399
+            $body = str_replace(
400
+                array( 'plural', 'n', '$n$plurals', ),
401
+                array( '$plural', '$n', '$nplurals', ),
402
+                'nplurals='. $nplurals . '; plural=' . $plural
403
+            );
404
+
405
+            // add parents
406
+            // important since PHP's ternary evaluates from left to right
407
+            $body .= ';';
408
+            $res = '';
409
+            $p = 0;
410
+            for($i = 0; $i < strlen($body); $i++) {
411
+                $ch = $body[$i];
412
+                switch ( $ch ) {
413
+                    case '?':
414
+                        $res .= ' ? (';
415
+                        $p++;
416
+                        break;
417
+                    case ':':
418
+                        $res .= ') : (';
419
+                        break;
420
+                    case ';':
421
+                        $res .= str_repeat( ')', $p ) . ';';
422
+                        $p = 0;
423
+                        break;
424
+                    default:
425
+                        $res .= $ch;
426
+                }
427
+            }
428
+
429
+            $body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
430
+            $function = create_function('$n', $body);
431
+            $this->pluralFunctions[$string] = $function;
432
+            return $function;
433
+        } else {
434
+            // default: one plural form for all cases but n==1 (english)
435
+            $function = create_function(
436
+                '$n',
437
+                '$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
438
+            );
439
+            $this->pluralFunctions[$string] = $function;
440
+            return $function;
441
+        }
442
+    }
443 443
 }
Please login to merge, or discard this patch.