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