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