Passed
Push — master ( 108051...ff08b1 )
by Roeland
53:17 queued 23:58
created
lib/private/L10N/Factory.php 2 patches
Indentation   +637 added lines, -637 removed lines patch added patch discarded remove patch
@@ -50,641 +50,641 @@
 block discarded – undo
50 50
  */
51 51
 class Factory implements IFactory {
52 52
 
53
-	/** @var string */
54
-	protected $requestLanguage = '';
55
-
56
-	/**
57
-	 * cached instances
58
-	 * @var array Structure: Lang => App => \OCP\IL10N
59
-	 */
60
-	protected $instances = [];
61
-
62
-	/**
63
-	 * @var array Structure: App => string[]
64
-	 */
65
-	protected $availableLanguages = [];
66
-
67
-	/**
68
-	 * @var Set
69
-	 */
70
-	protected $localeCache;
71
-
72
-	/**
73
-	 * @var array
74
-	 */
75
-	protected $availableLocales = [];
76
-
77
-	/**
78
-	 * @var array Structure: string => callable
79
-	 */
80
-	protected $pluralFunctions = [];
81
-
82
-	public const COMMON_LANGUAGE_CODES = [
83
-		'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
84
-		'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
85
-	];
86
-
87
-	/** @var IConfig */
88
-	protected $config;
89
-
90
-	/** @var IRequest */
91
-	protected $request;
92
-
93
-	/** @var IUserSession */
94
-	protected $userSession;
95
-
96
-	/** @var string */
97
-	protected $serverRoot;
98
-
99
-	/**
100
-	 * @param IConfig $config
101
-	 * @param IRequest $request
102
-	 * @param IUserSession $userSession
103
-	 * @param string $serverRoot
104
-	 */
105
-	public function __construct(IConfig $config,
106
-								IRequest $request,
107
-								IUserSession $userSession,
108
-								$serverRoot) {
109
-		$this->config = $config;
110
-		$this->request = $request;
111
-		$this->userSession = $userSession;
112
-		$this->serverRoot = $serverRoot;
113
-		$this->localeCache = new Set();
114
-	}
115
-
116
-	/**
117
-	 * Get a language instance
118
-	 *
119
-	 * @param string $app
120
-	 * @param string|null $lang
121
-	 * @param string|null $locale
122
-	 * @return \OCP\IL10N
123
-	 */
124
-	public function get($app, $lang = null, $locale = null) {
125
-		return new LazyL10N(function () use ($app, $lang, $locale) {
126
-			$app = \OC_App::cleanAppId($app);
127
-			if ($lang !== null) {
128
-				$lang = str_replace(['\0', '/', '\\', '..'], '', (string)$lang);
129
-			}
130
-
131
-			$forceLang = $this->config->getSystemValue('force_language', false);
132
-			if (is_string($forceLang)) {
133
-				$lang = $forceLang;
134
-			}
135
-
136
-			$forceLocale = $this->config->getSystemValue('force_locale', false);
137
-			if (is_string($forceLocale)) {
138
-				$locale = $forceLocale;
139
-			}
140
-
141
-			if ($lang === null || !$this->languageExists($app, $lang)) {
142
-				$lang = $this->findLanguage($app);
143
-			}
144
-
145
-			if ($locale === null || !$this->localeExists($locale)) {
146
-				$locale = $this->findLocale($lang);
147
-			}
148
-
149
-			if (!isset($this->instances[$lang][$app])) {
150
-				$this->instances[$lang][$app] = new L10N(
151
-					$this, $app, $lang, $locale,
152
-					$this->getL10nFilesForApp($app, $lang)
153
-				);
154
-			}
155
-
156
-			return $this->instances[$lang][$app];
157
-		});
158
-	}
159
-
160
-	/**
161
-	 * Find the best language
162
-	 *
163
-	 * @param string|null $app App id or null for core
164
-	 * @return string language If nothing works it returns 'en'
165
-	 */
166
-	public function findLanguage($app = null) {
167
-		$forceLang = $this->config->getSystemValue('force_language', false);
168
-		if (is_string($forceLang)) {
169
-			$this->requestLanguage = $forceLang;
170
-		}
171
-
172
-		if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
173
-			return $this->requestLanguage;
174
-		}
175
-
176
-		/**
177
-		 * At this point Nextcloud might not yet be installed and thus the lookup
178
-		 * in the preferences table might fail. For this reason we need to check
179
-		 * whether the instance has already been installed
180
-		 *
181
-		 * @link https://github.com/owncloud/core/issues/21955
182
-		 */
183
-		if ($this->config->getSystemValue('installed', false)) {
184
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
185
-			if (!is_null($userId)) {
186
-				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
187
-			} else {
188
-				$userLang = null;
189
-			}
190
-		} else {
191
-			$userId = null;
192
-			$userLang = null;
193
-		}
194
-
195
-		if ($userLang) {
196
-			$this->requestLanguage = $userLang;
197
-			if ($this->languageExists($app, $userLang)) {
198
-				return $userLang;
199
-			}
200
-		}
201
-
202
-		try {
203
-			// Try to get the language from the Request
204
-			$lang = $this->getLanguageFromRequest($app);
205
-			if ($userId !== null && $app === null && !$userLang) {
206
-				$this->config->setUserValue($userId, 'core', 'lang', $lang);
207
-			}
208
-			return $lang;
209
-		} catch (LanguageNotFoundException $e) {
210
-			// Finding language from request failed fall back to default language
211
-			$defaultLanguage = $this->config->getSystemValue('default_language', false);
212
-			if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
213
-				return $defaultLanguage;
214
-			}
215
-		}
216
-
217
-		// We could not find any language so fall back to english
218
-		return 'en';
219
-	}
220
-
221
-	/**
222
-	 * find the best locale
223
-	 *
224
-	 * @param string $lang
225
-	 * @return null|string
226
-	 */
227
-	public function findLocale($lang = null) {
228
-		$forceLocale = $this->config->getSystemValue('force_locale', false);
229
-		if (is_string($forceLocale) && $this->localeExists($forceLocale)) {
230
-			return $forceLocale;
231
-		}
232
-
233
-		if ($this->config->getSystemValue('installed', false)) {
234
-			$userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() :  null;
235
-			$userLocale = null;
236
-			if (null !== $userId) {
237
-				$userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
238
-			}
239
-		} else {
240
-			$userId = null;
241
-			$userLocale = null;
242
-		}
243
-
244
-		if ($userLocale && $this->localeExists($userLocale)) {
245
-			return $userLocale;
246
-		}
247
-
248
-		// Default : use system default locale
249
-		$defaultLocale = $this->config->getSystemValue('default_locale', false);
250
-		if ($defaultLocale !== false && $this->localeExists($defaultLocale)) {
251
-			return $defaultLocale;
252
-		}
253
-
254
-		// If no user locale set, use lang as locale
255
-		if (null !== $lang && $this->localeExists($lang)) {
256
-			return $lang;
257
-		}
258
-
259
-		// At last, return USA
260
-		return 'en_US';
261
-	}
262
-
263
-	/**
264
-	 * find the matching lang from the locale
265
-	 *
266
-	 * @param string $app
267
-	 * @param string $locale
268
-	 * @return null|string
269
-	 */
270
-	public function findLanguageFromLocale(string $app = 'core', string $locale = null) {
271
-		if ($this->languageExists($app, $locale)) {
272
-			return $locale;
273
-		}
274
-
275
-		// Try to split e.g: fr_FR => fr
276
-		$locale = explode('_', $locale)[0];
277
-		if ($this->languageExists($app, $locale)) {
278
-			return $locale;
279
-		}
280
-	}
281
-
282
-	/**
283
-	 * Find all available languages for an app
284
-	 *
285
-	 * @param string|null $app App id or null for core
286
-	 * @return array an array of available languages
287
-	 */
288
-	public function findAvailableLanguages($app = null) {
289
-		$key = $app;
290
-		if ($key === null) {
291
-			$key = 'null';
292
-		}
293
-
294
-		// also works with null as key
295
-		if (!empty($this->availableLanguages[$key])) {
296
-			return $this->availableLanguages[$key];
297
-		}
298
-
299
-		$available = ['en']; //english is always available
300
-		$dir = $this->findL10nDir($app);
301
-		if (is_dir($dir)) {
302
-			$files = scandir($dir);
303
-			if ($files !== false) {
304
-				foreach ($files as $file) {
305
-					if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
306
-						$available[] = substr($file, 0, -5);
307
-					}
308
-				}
309
-			}
310
-		}
311
-
312
-		// merge with translations from theme
313
-		$theme = $this->config->getSystemValue('theme');
314
-		if (!empty($theme)) {
315
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
316
-
317
-			if (is_dir($themeDir)) {
318
-				$files = scandir($themeDir);
319
-				if ($files !== false) {
320
-					foreach ($files as $file) {
321
-						if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
322
-							$available[] = substr($file, 0, -5);
323
-						}
324
-					}
325
-				}
326
-			}
327
-		}
328
-
329
-		$this->availableLanguages[$key] = $available;
330
-		return $available;
331
-	}
332
-
333
-	/**
334
-	 * @return array|mixed
335
-	 */
336
-	public function findAvailableLocales() {
337
-		if (!empty($this->availableLocales)) {
338
-			return $this->availableLocales;
339
-		}
340
-
341
-		$localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
342
-		$this->availableLocales = \json_decode($localeData, true);
343
-
344
-		return $this->availableLocales;
345
-	}
346
-
347
-	/**
348
-	 * @param string|null $app App id or null for core
349
-	 * @param string $lang
350
-	 * @return bool
351
-	 */
352
-	public function languageExists($app, $lang) {
353
-		if ($lang === 'en') {//english is always available
354
-			return true;
355
-		}
356
-
357
-		$languages = $this->findAvailableLanguages($app);
358
-		return array_search($lang, $languages) !== false;
359
-	}
360
-
361
-	public function getLanguageIterator(IUser $user = null): ILanguageIterator {
362
-		$user = $user ?? $this->userSession->getUser();
363
-		if ($user === null) {
364
-			throw new \RuntimeException('Failed to get an IUser instance');
365
-		}
366
-		return new LanguageIterator($user, $this->config);
367
-	}
368
-
369
-	/**
370
-	 * Return the language to use when sending something to a user
371
-	 *
372
-	 * @param IUser|null $user
373
-	 * @return string
374
-	 * @since 20.0.0
375
-	 */
376
-	public function getUserLanguage(IUser $user = null): string {
377
-		$language = $this->config->getSystemValue('force_language', false);
378
-		if ($language !== false) {
379
-			return $language;
380
-		}
381
-
382
-		if ($user instanceof IUser) {
383
-			$language = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
384
-			if ($language !== null) {
385
-				return $language;
386
-			}
387
-		}
388
-
389
-		return $this->config->getSystemValue('default_language', 'en');
390
-	}
391
-
392
-	/**
393
-	 * @param string $locale
394
-	 * @return bool
395
-	 */
396
-	public function localeExists($locale) {
397
-		if ($locale === 'en') { //english is always available
398
-			return true;
399
-		}
400
-
401
-		if ($this->localeCache->isEmpty()) {
402
-			$locales = $this->findAvailableLocales();
403
-			foreach ($locales as $l) {
404
-				$this->localeCache->add($l['code']);
405
-			}
406
-		}
407
-
408
-		return $this->localeCache->contains($locale);
409
-	}
410
-
411
-	/**
412
-	 * @param string|null $app
413
-	 * @return string
414
-	 * @throws LanguageNotFoundException
415
-	 */
416
-	private function getLanguageFromRequest($app) {
417
-		$header = $this->request->getHeader('ACCEPT_LANGUAGE');
418
-		if ($header !== '') {
419
-			$available = $this->findAvailableLanguages($app);
420
-
421
-			// E.g. make sure that 'de' is before 'de_DE'.
422
-			sort($available);
423
-
424
-			$preferences = preg_split('/,\s*/', strtolower($header));
425
-			foreach ($preferences as $preference) {
426
-				list($preferred_language) = explode(';', $preference);
427
-				$preferred_language = str_replace('-', '_', $preferred_language);
428
-
429
-				foreach ($available as $available_language) {
430
-					if ($preferred_language === strtolower($available_language)) {
431
-						return $this->respectDefaultLanguage($app, $available_language);
432
-					}
433
-				}
434
-
435
-				// Fallback from de_De to de
436
-				foreach ($available as $available_language) {
437
-					if (substr($preferred_language, 0, 2) === $available_language) {
438
-						return $available_language;
439
-					}
440
-				}
441
-			}
442
-		}
443
-
444
-		throw new LanguageNotFoundException();
445
-	}
446
-
447
-	/**
448
-	 * if default language is set to de_DE (formal German) this should be
449
-	 * preferred to 'de' (non-formal German) if possible
450
-	 *
451
-	 * @param string|null $app
452
-	 * @param string $lang
453
-	 * @return string
454
-	 */
455
-	protected function respectDefaultLanguage($app, $lang) {
456
-		$result = $lang;
457
-		$defaultLanguage = $this->config->getSystemValue('default_language', false);
458
-
459
-		// use formal version of german ("Sie" instead of "Du") if the default
460
-		// language is set to 'de_DE' if possible
461
-		if (is_string($defaultLanguage) &&
462
-			strtolower($lang) === 'de' &&
463
-			strtolower($defaultLanguage) === 'de_de' &&
464
-			$this->languageExists($app, 'de_DE')
465
-		) {
466
-			$result = 'de_DE';
467
-		}
468
-
469
-		return $result;
470
-	}
471
-
472
-	/**
473
-	 * Checks if $sub is a subdirectory of $parent
474
-	 *
475
-	 * @param string $sub
476
-	 * @param string $parent
477
-	 * @return bool
478
-	 */
479
-	private function isSubDirectory($sub, $parent) {
480
-		// Check whether $sub contains no ".."
481
-		if (strpos($sub, '..') !== false) {
482
-			return false;
483
-		}
484
-
485
-		// Check whether $sub is a subdirectory of $parent
486
-		if (strpos($sub, $parent) === 0) {
487
-			return true;
488
-		}
489
-
490
-		return false;
491
-	}
492
-
493
-	/**
494
-	 * Get a list of language files that should be loaded
495
-	 *
496
-	 * @param string $app
497
-	 * @param string $lang
498
-	 * @return string[]
499
-	 */
500
-	// FIXME This method is only public, until OC_L10N does not need it anymore,
501
-	// FIXME This is also the reason, why it is not in the public interface
502
-	public function getL10nFilesForApp($app, $lang) {
503
-		$languageFiles = [];
504
-
505
-		$i18nDir = $this->findL10nDir($app);
506
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
507
-
508
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
509
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
510
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
511
-			)
512
-			&& file_exists($transFile)) {
513
-			// load the translations file
514
-			$languageFiles[] = $transFile;
515
-		}
516
-
517
-		// merge with translations from theme
518
-		$theme = $this->config->getSystemValue('theme');
519
-		if (!empty($theme)) {
520
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
521
-			if (file_exists($transFile)) {
522
-				$languageFiles[] = $transFile;
523
-			}
524
-		}
525
-
526
-		return $languageFiles;
527
-	}
528
-
529
-	/**
530
-	 * find the l10n directory
531
-	 *
532
-	 * @param string $app App id or empty string for core
533
-	 * @return string directory
534
-	 */
535
-	protected function findL10nDir($app = null) {
536
-		if (in_array($app, ['core', 'lib'])) {
537
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
538
-				return $this->serverRoot . '/' . $app . '/l10n/';
539
-			}
540
-		} elseif ($app && \OC_App::getAppPath($app) !== false) {
541
-			// Check if the app is in the app folder
542
-			return \OC_App::getAppPath($app) . '/l10n/';
543
-		}
544
-		return $this->serverRoot . '/core/l10n/';
545
-	}
546
-
547
-
548
-	/**
549
-	 * Creates a function from the plural string
550
-	 *
551
-	 * Parts of the code is copied from Habari:
552
-	 * https://github.com/habari/system/blob/master/classes/locale.php
553
-	 * @param string $string
554
-	 * @return string
555
-	 */
556
-	public function createPluralFunction($string) {
557
-		if (isset($this->pluralFunctions[$string])) {
558
-			return $this->pluralFunctions[$string];
559
-		}
560
-
561
-		if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
562
-			// sanitize
563
-			$nplurals = preg_replace('/[^0-9]/', '', $matches[1]);
564
-			$plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
565
-
566
-			$body = str_replace(
567
-				[ 'plural', 'n', '$n$plurals', ],
568
-				[ '$plural', '$n', '$nplurals', ],
569
-				'nplurals='. $nplurals . '; plural=' . $plural
570
-			);
571
-
572
-			// add parents
573
-			// important since PHP's ternary evaluates from left to right
574
-			$body .= ';';
575
-			$res = '';
576
-			$p = 0;
577
-			$length = strlen($body);
578
-			for ($i = 0; $i < $length; $i++) {
579
-				$ch = $body[$i];
580
-				switch ($ch) {
581
-					case '?':
582
-						$res .= ' ? (';
583
-						$p++;
584
-						break;
585
-					case ':':
586
-						$res .= ') : (';
587
-						break;
588
-					case ';':
589
-						$res .= str_repeat(')', $p) . ';';
590
-						$p = 0;
591
-						break;
592
-					default:
593
-						$res .= $ch;
594
-				}
595
-			}
596
-
597
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
598
-			$function = create_function('$n', $body);
599
-			$this->pluralFunctions[$string] = $function;
600
-			return $function;
601
-		} else {
602
-			// default: one plural form for all cases but n==1 (english)
603
-			$function = create_function(
604
-				'$n',
605
-				'$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
606
-			);
607
-			$this->pluralFunctions[$string] = $function;
608
-			return $function;
609
-		}
610
-	}
611
-
612
-	/**
613
-	 * returns the common language and other languages in an
614
-	 * associative array
615
-	 *
616
-	 * @return array
617
-	 */
618
-	public function getLanguages() {
619
-		$forceLanguage = $this->config->getSystemValue('force_language', false);
620
-		if ($forceLanguage !== false) {
621
-			$l = $this->get('lib', $forceLanguage);
622
-			$potentialName = (string) $l->t('__language_name__');
623
-
624
-			return [
625
-				'commonlanguages' => [[
626
-					'code' => $forceLanguage,
627
-					'name' => $potentialName,
628
-				]],
629
-				'languages' => [],
630
-			];
631
-		}
632
-
633
-		$languageCodes = $this->findAvailableLanguages();
634
-
635
-		$commonLanguages = [];
636
-		$languages = [];
637
-
638
-		foreach ($languageCodes as $lang) {
639
-			$l = $this->get('lib', $lang);
640
-			// TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
641
-			$potentialName = (string) $l->t('__language_name__');
642
-			if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
643
-				$ln = [
644
-					'code' => $lang,
645
-					'name' => $potentialName
646
-				];
647
-			} elseif ($lang === 'en') {
648
-				$ln = [
649
-					'code' => $lang,
650
-					'name' => 'English (US)'
651
-				];
652
-			} else {//fallback to language code
653
-				$ln = [
654
-					'code' => $lang,
655
-					'name' => $lang
656
-				];
657
-			}
658
-
659
-			// put appropriate languages into appropriate arrays, to print them sorted
660
-			// common languages -> divider -> other languages
661
-			if (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
662
-				$commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln;
663
-			} else {
664
-				$languages[] = $ln;
665
-			}
666
-		}
667
-
668
-		ksort($commonLanguages);
669
-
670
-		// sort now by displayed language not the iso-code
671
-		usort($languages, function ($a, $b) {
672
-			if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
673
-				// If a doesn't have a name, but b does, list b before a
674
-				return 1;
675
-			}
676
-			if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
677
-				// If a does have a name, but b doesn't, list a before b
678
-				return -1;
679
-			}
680
-			// Otherwise compare the names
681
-			return strcmp($a['name'], $b['name']);
682
-		});
683
-
684
-		return [
685
-			// reset indexes
686
-			'commonlanguages' => array_values($commonLanguages),
687
-			'languages' => $languages
688
-		];
689
-	}
53
+    /** @var string */
54
+    protected $requestLanguage = '';
55
+
56
+    /**
57
+     * cached instances
58
+     * @var array Structure: Lang => App => \OCP\IL10N
59
+     */
60
+    protected $instances = [];
61
+
62
+    /**
63
+     * @var array Structure: App => string[]
64
+     */
65
+    protected $availableLanguages = [];
66
+
67
+    /**
68
+     * @var Set
69
+     */
70
+    protected $localeCache;
71
+
72
+    /**
73
+     * @var array
74
+     */
75
+    protected $availableLocales = [];
76
+
77
+    /**
78
+     * @var array Structure: string => callable
79
+     */
80
+    protected $pluralFunctions = [];
81
+
82
+    public const COMMON_LANGUAGE_CODES = [
83
+        'en', 'es', 'fr', 'de', 'de_DE', 'ja', 'ar', 'ru', 'nl', 'it',
84
+        'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko'
85
+    ];
86
+
87
+    /** @var IConfig */
88
+    protected $config;
89
+
90
+    /** @var IRequest */
91
+    protected $request;
92
+
93
+    /** @var IUserSession */
94
+    protected $userSession;
95
+
96
+    /** @var string */
97
+    protected $serverRoot;
98
+
99
+    /**
100
+     * @param IConfig $config
101
+     * @param IRequest $request
102
+     * @param IUserSession $userSession
103
+     * @param string $serverRoot
104
+     */
105
+    public function __construct(IConfig $config,
106
+                                IRequest $request,
107
+                                IUserSession $userSession,
108
+                                $serverRoot) {
109
+        $this->config = $config;
110
+        $this->request = $request;
111
+        $this->userSession = $userSession;
112
+        $this->serverRoot = $serverRoot;
113
+        $this->localeCache = new Set();
114
+    }
115
+
116
+    /**
117
+     * Get a language instance
118
+     *
119
+     * @param string $app
120
+     * @param string|null $lang
121
+     * @param string|null $locale
122
+     * @return \OCP\IL10N
123
+     */
124
+    public function get($app, $lang = null, $locale = null) {
125
+        return new LazyL10N(function () use ($app, $lang, $locale) {
126
+            $app = \OC_App::cleanAppId($app);
127
+            if ($lang !== null) {
128
+                $lang = str_replace(['\0', '/', '\\', '..'], '', (string)$lang);
129
+            }
130
+
131
+            $forceLang = $this->config->getSystemValue('force_language', false);
132
+            if (is_string($forceLang)) {
133
+                $lang = $forceLang;
134
+            }
135
+
136
+            $forceLocale = $this->config->getSystemValue('force_locale', false);
137
+            if (is_string($forceLocale)) {
138
+                $locale = $forceLocale;
139
+            }
140
+
141
+            if ($lang === null || !$this->languageExists($app, $lang)) {
142
+                $lang = $this->findLanguage($app);
143
+            }
144
+
145
+            if ($locale === null || !$this->localeExists($locale)) {
146
+                $locale = $this->findLocale($lang);
147
+            }
148
+
149
+            if (!isset($this->instances[$lang][$app])) {
150
+                $this->instances[$lang][$app] = new L10N(
151
+                    $this, $app, $lang, $locale,
152
+                    $this->getL10nFilesForApp($app, $lang)
153
+                );
154
+            }
155
+
156
+            return $this->instances[$lang][$app];
157
+        });
158
+    }
159
+
160
+    /**
161
+     * Find the best language
162
+     *
163
+     * @param string|null $app App id or null for core
164
+     * @return string language If nothing works it returns 'en'
165
+     */
166
+    public function findLanguage($app = null) {
167
+        $forceLang = $this->config->getSystemValue('force_language', false);
168
+        if (is_string($forceLang)) {
169
+            $this->requestLanguage = $forceLang;
170
+        }
171
+
172
+        if ($this->requestLanguage !== '' && $this->languageExists($app, $this->requestLanguage)) {
173
+            return $this->requestLanguage;
174
+        }
175
+
176
+        /**
177
+         * At this point Nextcloud might not yet be installed and thus the lookup
178
+         * in the preferences table might fail. For this reason we need to check
179
+         * whether the instance has already been installed
180
+         *
181
+         * @link https://github.com/owncloud/core/issues/21955
182
+         */
183
+        if ($this->config->getSystemValue('installed', false)) {
184
+            $userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
185
+            if (!is_null($userId)) {
186
+                $userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
187
+            } else {
188
+                $userLang = null;
189
+            }
190
+        } else {
191
+            $userId = null;
192
+            $userLang = null;
193
+        }
194
+
195
+        if ($userLang) {
196
+            $this->requestLanguage = $userLang;
197
+            if ($this->languageExists($app, $userLang)) {
198
+                return $userLang;
199
+            }
200
+        }
201
+
202
+        try {
203
+            // Try to get the language from the Request
204
+            $lang = $this->getLanguageFromRequest($app);
205
+            if ($userId !== null && $app === null && !$userLang) {
206
+                $this->config->setUserValue($userId, 'core', 'lang', $lang);
207
+            }
208
+            return $lang;
209
+        } catch (LanguageNotFoundException $e) {
210
+            // Finding language from request failed fall back to default language
211
+            $defaultLanguage = $this->config->getSystemValue('default_language', false);
212
+            if ($defaultLanguage !== false && $this->languageExists($app, $defaultLanguage)) {
213
+                return $defaultLanguage;
214
+            }
215
+        }
216
+
217
+        // We could not find any language so fall back to english
218
+        return 'en';
219
+    }
220
+
221
+    /**
222
+     * find the best locale
223
+     *
224
+     * @param string $lang
225
+     * @return null|string
226
+     */
227
+    public function findLocale($lang = null) {
228
+        $forceLocale = $this->config->getSystemValue('force_locale', false);
229
+        if (is_string($forceLocale) && $this->localeExists($forceLocale)) {
230
+            return $forceLocale;
231
+        }
232
+
233
+        if ($this->config->getSystemValue('installed', false)) {
234
+            $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() :  null;
235
+            $userLocale = null;
236
+            if (null !== $userId) {
237
+                $userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
238
+            }
239
+        } else {
240
+            $userId = null;
241
+            $userLocale = null;
242
+        }
243
+
244
+        if ($userLocale && $this->localeExists($userLocale)) {
245
+            return $userLocale;
246
+        }
247
+
248
+        // Default : use system default locale
249
+        $defaultLocale = $this->config->getSystemValue('default_locale', false);
250
+        if ($defaultLocale !== false && $this->localeExists($defaultLocale)) {
251
+            return $defaultLocale;
252
+        }
253
+
254
+        // If no user locale set, use lang as locale
255
+        if (null !== $lang && $this->localeExists($lang)) {
256
+            return $lang;
257
+        }
258
+
259
+        // At last, return USA
260
+        return 'en_US';
261
+    }
262
+
263
+    /**
264
+     * find the matching lang from the locale
265
+     *
266
+     * @param string $app
267
+     * @param string $locale
268
+     * @return null|string
269
+     */
270
+    public function findLanguageFromLocale(string $app = 'core', string $locale = null) {
271
+        if ($this->languageExists($app, $locale)) {
272
+            return $locale;
273
+        }
274
+
275
+        // Try to split e.g: fr_FR => fr
276
+        $locale = explode('_', $locale)[0];
277
+        if ($this->languageExists($app, $locale)) {
278
+            return $locale;
279
+        }
280
+    }
281
+
282
+    /**
283
+     * Find all available languages for an app
284
+     *
285
+     * @param string|null $app App id or null for core
286
+     * @return array an array of available languages
287
+     */
288
+    public function findAvailableLanguages($app = null) {
289
+        $key = $app;
290
+        if ($key === null) {
291
+            $key = 'null';
292
+        }
293
+
294
+        // also works with null as key
295
+        if (!empty($this->availableLanguages[$key])) {
296
+            return $this->availableLanguages[$key];
297
+        }
298
+
299
+        $available = ['en']; //english is always available
300
+        $dir = $this->findL10nDir($app);
301
+        if (is_dir($dir)) {
302
+            $files = scandir($dir);
303
+            if ($files !== false) {
304
+                foreach ($files as $file) {
305
+                    if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
306
+                        $available[] = substr($file, 0, -5);
307
+                    }
308
+                }
309
+            }
310
+        }
311
+
312
+        // merge with translations from theme
313
+        $theme = $this->config->getSystemValue('theme');
314
+        if (!empty($theme)) {
315
+            $themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
316
+
317
+            if (is_dir($themeDir)) {
318
+                $files = scandir($themeDir);
319
+                if ($files !== false) {
320
+                    foreach ($files as $file) {
321
+                        if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') {
322
+                            $available[] = substr($file, 0, -5);
323
+                        }
324
+                    }
325
+                }
326
+            }
327
+        }
328
+
329
+        $this->availableLanguages[$key] = $available;
330
+        return $available;
331
+    }
332
+
333
+    /**
334
+     * @return array|mixed
335
+     */
336
+    public function findAvailableLocales() {
337
+        if (!empty($this->availableLocales)) {
338
+            return $this->availableLocales;
339
+        }
340
+
341
+        $localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
342
+        $this->availableLocales = \json_decode($localeData, true);
343
+
344
+        return $this->availableLocales;
345
+    }
346
+
347
+    /**
348
+     * @param string|null $app App id or null for core
349
+     * @param string $lang
350
+     * @return bool
351
+     */
352
+    public function languageExists($app, $lang) {
353
+        if ($lang === 'en') {//english is always available
354
+            return true;
355
+        }
356
+
357
+        $languages = $this->findAvailableLanguages($app);
358
+        return array_search($lang, $languages) !== false;
359
+    }
360
+
361
+    public function getLanguageIterator(IUser $user = null): ILanguageIterator {
362
+        $user = $user ?? $this->userSession->getUser();
363
+        if ($user === null) {
364
+            throw new \RuntimeException('Failed to get an IUser instance');
365
+        }
366
+        return new LanguageIterator($user, $this->config);
367
+    }
368
+
369
+    /**
370
+     * Return the language to use when sending something to a user
371
+     *
372
+     * @param IUser|null $user
373
+     * @return string
374
+     * @since 20.0.0
375
+     */
376
+    public function getUserLanguage(IUser $user = null): string {
377
+        $language = $this->config->getSystemValue('force_language', false);
378
+        if ($language !== false) {
379
+            return $language;
380
+        }
381
+
382
+        if ($user instanceof IUser) {
383
+            $language = $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
384
+            if ($language !== null) {
385
+                return $language;
386
+            }
387
+        }
388
+
389
+        return $this->config->getSystemValue('default_language', 'en');
390
+    }
391
+
392
+    /**
393
+     * @param string $locale
394
+     * @return bool
395
+     */
396
+    public function localeExists($locale) {
397
+        if ($locale === 'en') { //english is always available
398
+            return true;
399
+        }
400
+
401
+        if ($this->localeCache->isEmpty()) {
402
+            $locales = $this->findAvailableLocales();
403
+            foreach ($locales as $l) {
404
+                $this->localeCache->add($l['code']);
405
+            }
406
+        }
407
+
408
+        return $this->localeCache->contains($locale);
409
+    }
410
+
411
+    /**
412
+     * @param string|null $app
413
+     * @return string
414
+     * @throws LanguageNotFoundException
415
+     */
416
+    private function getLanguageFromRequest($app) {
417
+        $header = $this->request->getHeader('ACCEPT_LANGUAGE');
418
+        if ($header !== '') {
419
+            $available = $this->findAvailableLanguages($app);
420
+
421
+            // E.g. make sure that 'de' is before 'de_DE'.
422
+            sort($available);
423
+
424
+            $preferences = preg_split('/,\s*/', strtolower($header));
425
+            foreach ($preferences as $preference) {
426
+                list($preferred_language) = explode(';', $preference);
427
+                $preferred_language = str_replace('-', '_', $preferred_language);
428
+
429
+                foreach ($available as $available_language) {
430
+                    if ($preferred_language === strtolower($available_language)) {
431
+                        return $this->respectDefaultLanguage($app, $available_language);
432
+                    }
433
+                }
434
+
435
+                // Fallback from de_De to de
436
+                foreach ($available as $available_language) {
437
+                    if (substr($preferred_language, 0, 2) === $available_language) {
438
+                        return $available_language;
439
+                    }
440
+                }
441
+            }
442
+        }
443
+
444
+        throw new LanguageNotFoundException();
445
+    }
446
+
447
+    /**
448
+     * if default language is set to de_DE (formal German) this should be
449
+     * preferred to 'de' (non-formal German) if possible
450
+     *
451
+     * @param string|null $app
452
+     * @param string $lang
453
+     * @return string
454
+     */
455
+    protected function respectDefaultLanguage($app, $lang) {
456
+        $result = $lang;
457
+        $defaultLanguage = $this->config->getSystemValue('default_language', false);
458
+
459
+        // use formal version of german ("Sie" instead of "Du") if the default
460
+        // language is set to 'de_DE' if possible
461
+        if (is_string($defaultLanguage) &&
462
+            strtolower($lang) === 'de' &&
463
+            strtolower($defaultLanguage) === 'de_de' &&
464
+            $this->languageExists($app, 'de_DE')
465
+        ) {
466
+            $result = 'de_DE';
467
+        }
468
+
469
+        return $result;
470
+    }
471
+
472
+    /**
473
+     * Checks if $sub is a subdirectory of $parent
474
+     *
475
+     * @param string $sub
476
+     * @param string $parent
477
+     * @return bool
478
+     */
479
+    private function isSubDirectory($sub, $parent) {
480
+        // Check whether $sub contains no ".."
481
+        if (strpos($sub, '..') !== false) {
482
+            return false;
483
+        }
484
+
485
+        // Check whether $sub is a subdirectory of $parent
486
+        if (strpos($sub, $parent) === 0) {
487
+            return true;
488
+        }
489
+
490
+        return false;
491
+    }
492
+
493
+    /**
494
+     * Get a list of language files that should be loaded
495
+     *
496
+     * @param string $app
497
+     * @param string $lang
498
+     * @return string[]
499
+     */
500
+    // FIXME This method is only public, until OC_L10N does not need it anymore,
501
+    // FIXME This is also the reason, why it is not in the public interface
502
+    public function getL10nFilesForApp($app, $lang) {
503
+        $languageFiles = [];
504
+
505
+        $i18nDir = $this->findL10nDir($app);
506
+        $transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
507
+
508
+        if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
509
+                || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
510
+                || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
511
+            )
512
+            && file_exists($transFile)) {
513
+            // load the translations file
514
+            $languageFiles[] = $transFile;
515
+        }
516
+
517
+        // merge with translations from theme
518
+        $theme = $this->config->getSystemValue('theme');
519
+        if (!empty($theme)) {
520
+            $transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
521
+            if (file_exists($transFile)) {
522
+                $languageFiles[] = $transFile;
523
+            }
524
+        }
525
+
526
+        return $languageFiles;
527
+    }
528
+
529
+    /**
530
+     * find the l10n directory
531
+     *
532
+     * @param string $app App id or empty string for core
533
+     * @return string directory
534
+     */
535
+    protected function findL10nDir($app = null) {
536
+        if (in_array($app, ['core', 'lib'])) {
537
+            if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
538
+                return $this->serverRoot . '/' . $app . '/l10n/';
539
+            }
540
+        } elseif ($app && \OC_App::getAppPath($app) !== false) {
541
+            // Check if the app is in the app folder
542
+            return \OC_App::getAppPath($app) . '/l10n/';
543
+        }
544
+        return $this->serverRoot . '/core/l10n/';
545
+    }
546
+
547
+
548
+    /**
549
+     * Creates a function from the plural string
550
+     *
551
+     * Parts of the code is copied from Habari:
552
+     * https://github.com/habari/system/blob/master/classes/locale.php
553
+     * @param string $string
554
+     * @return string
555
+     */
556
+    public function createPluralFunction($string) {
557
+        if (isset($this->pluralFunctions[$string])) {
558
+            return $this->pluralFunctions[$string];
559
+        }
560
+
561
+        if (preg_match('/^\s*nplurals\s*=\s*(\d+)\s*;\s*plural=(.*)$/u', $string, $matches)) {
562
+            // sanitize
563
+            $nplurals = preg_replace('/[^0-9]/', '', $matches[1]);
564
+            $plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
565
+
566
+            $body = str_replace(
567
+                [ 'plural', 'n', '$n$plurals', ],
568
+                [ '$plural', '$n', '$nplurals', ],
569
+                'nplurals='. $nplurals . '; plural=' . $plural
570
+            );
571
+
572
+            // add parents
573
+            // important since PHP's ternary evaluates from left to right
574
+            $body .= ';';
575
+            $res = '';
576
+            $p = 0;
577
+            $length = strlen($body);
578
+            for ($i = 0; $i < $length; $i++) {
579
+                $ch = $body[$i];
580
+                switch ($ch) {
581
+                    case '?':
582
+                        $res .= ' ? (';
583
+                        $p++;
584
+                        break;
585
+                    case ':':
586
+                        $res .= ') : (';
587
+                        break;
588
+                    case ';':
589
+                        $res .= str_repeat(')', $p) . ';';
590
+                        $p = 0;
591
+                        break;
592
+                    default:
593
+                        $res .= $ch;
594
+                }
595
+            }
596
+
597
+            $body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
598
+            $function = create_function('$n', $body);
599
+            $this->pluralFunctions[$string] = $function;
600
+            return $function;
601
+        } else {
602
+            // default: one plural form for all cases but n==1 (english)
603
+            $function = create_function(
604
+                '$n',
605
+                '$nplurals=2;$plural=($n==1?0:1);return ($plural>=$nplurals?$nplurals-1:$plural);'
606
+            );
607
+            $this->pluralFunctions[$string] = $function;
608
+            return $function;
609
+        }
610
+    }
611
+
612
+    /**
613
+     * returns the common language and other languages in an
614
+     * associative array
615
+     *
616
+     * @return array
617
+     */
618
+    public function getLanguages() {
619
+        $forceLanguage = $this->config->getSystemValue('force_language', false);
620
+        if ($forceLanguage !== false) {
621
+            $l = $this->get('lib', $forceLanguage);
622
+            $potentialName = (string) $l->t('__language_name__');
623
+
624
+            return [
625
+                'commonlanguages' => [[
626
+                    'code' => $forceLanguage,
627
+                    'name' => $potentialName,
628
+                ]],
629
+                'languages' => [],
630
+            ];
631
+        }
632
+
633
+        $languageCodes = $this->findAvailableLanguages();
634
+
635
+        $commonLanguages = [];
636
+        $languages = [];
637
+
638
+        foreach ($languageCodes as $lang) {
639
+            $l = $this->get('lib', $lang);
640
+            // TRANSLATORS this is the language name for the language switcher in the personal settings and should be the localized version
641
+            $potentialName = (string) $l->t('__language_name__');
642
+            if ($l->getLanguageCode() === $lang && $potentialName[0] !== '_') {//first check if the language name is in the translation file
643
+                $ln = [
644
+                    'code' => $lang,
645
+                    'name' => $potentialName
646
+                ];
647
+            } elseif ($lang === 'en') {
648
+                $ln = [
649
+                    'code' => $lang,
650
+                    'name' => 'English (US)'
651
+                ];
652
+            } else {//fallback to language code
653
+                $ln = [
654
+                    'code' => $lang,
655
+                    'name' => $lang
656
+                ];
657
+            }
658
+
659
+            // put appropriate languages into appropriate arrays, to print them sorted
660
+            // common languages -> divider -> other languages
661
+            if (in_array($lang, self::COMMON_LANGUAGE_CODES)) {
662
+                $commonLanguages[array_search($lang, self::COMMON_LANGUAGE_CODES)] = $ln;
663
+            } else {
664
+                $languages[] = $ln;
665
+            }
666
+        }
667
+
668
+        ksort($commonLanguages);
669
+
670
+        // sort now by displayed language not the iso-code
671
+        usort($languages, function ($a, $b) {
672
+            if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
673
+                // If a doesn't have a name, but b does, list b before a
674
+                return 1;
675
+            }
676
+            if ($a['code'] !== $a['name'] && $b['code'] === $b['name']) {
677
+                // If a does have a name, but b doesn't, list a before b
678
+                return -1;
679
+            }
680
+            // Otherwise compare the names
681
+            return strcmp($a['name'], $b['name']);
682
+        });
683
+
684
+        return [
685
+            // reset indexes
686
+            'commonlanguages' => array_values($commonLanguages),
687
+            'languages' => $languages
688
+        ];
689
+    }
690 690
 }
Please login to merge, or discard this patch.
Spacing   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -122,10 +122,10 @@  discard block
 block discarded – undo
122 122
 	 * @return \OCP\IL10N
123 123
 	 */
124 124
 	public function get($app, $lang = null, $locale = null) {
125
-		return new LazyL10N(function () use ($app, $lang, $locale) {
125
+		return new LazyL10N(function() use ($app, $lang, $locale) {
126 126
 			$app = \OC_App::cleanAppId($app);
127 127
 			if ($lang !== null) {
128
-				$lang = str_replace(['\0', '/', '\\', '..'], '', (string)$lang);
128
+				$lang = str_replace(['\0', '/', '\\', '..'], '', (string) $lang);
129 129
 			}
130 130
 
131 131
 			$forceLang = $this->config->getSystemValue('force_language', false);
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
 		 * @link https://github.com/owncloud/core/issues/21955
182 182
 		 */
183 183
 		if ($this->config->getSystemValue('installed', false)) {
184
-			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() :  null;
184
+			$userId = !is_null($this->userSession->getUser()) ? $this->userSession->getUser()->getUID() : null;
185 185
 			if (!is_null($userId)) {
186 186
 				$userLang = $this->config->getUserValue($userId, 'core', 'lang', null);
187 187
 			} else {
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
 		}
232 232
 
233 233
 		if ($this->config->getSystemValue('installed', false)) {
234
-			$userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() :  null;
234
+			$userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null;
235 235
 			$userLocale = null;
236 236
 			if (null !== $userId) {
237 237
 				$userLocale = $this->config->getUserValue($userId, 'core', 'locale', null);
@@ -312,7 +312,7 @@  discard block
 block discarded – undo
312 312
 		// merge with translations from theme
313 313
 		$theme = $this->config->getSystemValue('theme');
314 314
 		if (!empty($theme)) {
315
-			$themeDir = $this->serverRoot . '/themes/' . $theme . substr($dir, strlen($this->serverRoot));
315
+			$themeDir = $this->serverRoot.'/themes/'.$theme.substr($dir, strlen($this->serverRoot));
316 316
 
317 317
 			if (is_dir($themeDir)) {
318 318
 				$files = scandir($themeDir);
@@ -338,7 +338,7 @@  discard block
 block discarded – undo
338 338
 			return $this->availableLocales;
339 339
 		}
340 340
 
341
-		$localeData = file_get_contents(\OC::$SERVERROOT . '/resources/locales.json');
341
+		$localeData = file_get_contents(\OC::$SERVERROOT.'/resources/locales.json');
342 342
 		$this->availableLocales = \json_decode($localeData, true);
343 343
 
344 344
 		return $this->availableLocales;
@@ -503,11 +503,11 @@  discard block
 block discarded – undo
503 503
 		$languageFiles = [];
504 504
 
505 505
 		$i18nDir = $this->findL10nDir($app);
506
-		$transFile = strip_tags($i18nDir) . strip_tags($lang) . '.json';
506
+		$transFile = strip_tags($i18nDir).strip_tags($lang).'.json';
507 507
 
508
-		if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/')
509
-				|| $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/')
510
-				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')
508
+		if (($this->isSubDirectory($transFile, $this->serverRoot.'/core/l10n/')
509
+				|| $this->isSubDirectory($transFile, $this->serverRoot.'/lib/l10n/')
510
+				|| $this->isSubDirectory($transFile, \OC_App::getAppPath($app).'/l10n/')
511 511
 			)
512 512
 			&& file_exists($transFile)) {
513 513
 			// load the translations file
@@ -517,7 +517,7 @@  discard block
 block discarded – undo
517 517
 		// merge with translations from theme
518 518
 		$theme = $this->config->getSystemValue('theme');
519 519
 		if (!empty($theme)) {
520
-			$transFile = $this->serverRoot . '/themes/' . $theme . substr($transFile, strlen($this->serverRoot));
520
+			$transFile = $this->serverRoot.'/themes/'.$theme.substr($transFile, strlen($this->serverRoot));
521 521
 			if (file_exists($transFile)) {
522 522
 				$languageFiles[] = $transFile;
523 523
 			}
@@ -534,14 +534,14 @@  discard block
 block discarded – undo
534 534
 	 */
535 535
 	protected function findL10nDir($app = null) {
536 536
 		if (in_array($app, ['core', 'lib'])) {
537
-			if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) {
538
-				return $this->serverRoot . '/' . $app . '/l10n/';
537
+			if (file_exists($this->serverRoot.'/'.$app.'/l10n/')) {
538
+				return $this->serverRoot.'/'.$app.'/l10n/';
539 539
 			}
540 540
 		} elseif ($app && \OC_App::getAppPath($app) !== false) {
541 541
 			// Check if the app is in the app folder
542
-			return \OC_App::getAppPath($app) . '/l10n/';
542
+			return \OC_App::getAppPath($app).'/l10n/';
543 543
 		}
544
-		return $this->serverRoot . '/core/l10n/';
544
+		return $this->serverRoot.'/core/l10n/';
545 545
 	}
546 546
 
547 547
 
@@ -564,9 +564,9 @@  discard block
 block discarded – undo
564 564
 			$plural = preg_replace('#[^n0-9:\(\)\?\|\&=!<>+*/\%-]#', '', $matches[2]);
565 565
 
566 566
 			$body = str_replace(
567
-				[ 'plural', 'n', '$n$plurals', ],
568
-				[ '$plural', '$n', '$nplurals', ],
569
-				'nplurals='. $nplurals . '; plural=' . $plural
567
+				['plural', 'n', '$n$plurals', ],
568
+				['$plural', '$n', '$nplurals', ],
569
+				'nplurals='.$nplurals.'; plural='.$plural
570 570
 			);
571 571
 
572 572
 			// add parents
@@ -586,7 +586,7 @@  discard block
 block discarded – undo
586 586
 						$res .= ') : (';
587 587
 						break;
588 588
 					case ';':
589
-						$res .= str_repeat(')', $p) . ';';
589
+						$res .= str_repeat(')', $p).';';
590 590
 						$p = 0;
591 591
 						break;
592 592
 					default:
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
 				}
595 595
 			}
596 596
 
597
-			$body = $res . 'return ($plural>=$nplurals?$nplurals-1:$plural);';
597
+			$body = $res.'return ($plural>=$nplurals?$nplurals-1:$plural);';
598 598
 			$function = create_function('$n', $body);
599 599
 			$this->pluralFunctions[$string] = $function;
600 600
 			return $function;
@@ -668,7 +668,7 @@  discard block
 block discarded – undo
668 668
 		ksort($commonLanguages);
669 669
 
670 670
 		// sort now by displayed language not the iso-code
671
-		usort($languages, function ($a, $b) {
671
+		usort($languages, function($a, $b) {
672 672
 			if ($a['code'] === $a['name'] && $b['code'] !== $b['name']) {
673 673
 				// If a doesn't have a name, but b does, list b before a
674 674
 				return 1;
Please login to merge, or discard this patch.