PageRenderTime 125ms CodeModel.GetById 41ms app.highlight 15ms RepoModel.GetById 65ms app.codeStats 0ms

/lib/l10n.php

https://github.com/sezuan/core
PHP | 383 lines | 205 code | 26 blank | 152 comment | 50 complexity | e2dc391072ca191108ae249555ee8771 MD5 | raw file
  1<?php
  2/**
  3 * ownCloud
  4 *
  5 * @author Jakob Sack
  6 * @copyright 2012 Frank Karlitschek frank@owncloud.org
  7 *
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
 10 * License as published by the Free Software Foundation; either
 11 * version 3 of the License, or any later version.
 12 *
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
 17 *
 18 * You should have received a copy of the GNU Affero General Public
 19 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 20 *
 21 */
 22
 23/**
 24 * This class is for i18n and l10n
 25 */
 26class OC_L10N{
 27	/**
 28	 * cached instances
 29	 */
 30	protected static $instances=array();
 31
 32	/**
 33	 * cache
 34	 */
 35	protected static $cache = array();
 36
 37	/**
 38	 * The best language
 39	 */
 40	protected static $language = '';
 41
 42	/**
 43	 * App of this object
 44	 */
 45	protected $app;
 46
 47	/**
 48	 * Language of this object
 49	 */
 50	protected $lang;
 51
 52	/**
 53	 * Translations
 54	 */
 55	private $translations = array();
 56
 57	/**
 58	 * Localization
 59	 */
 60	private $localizations = array(
 61		'jsdate' => 'dd.mm.yy',
 62		'date' => '%d.%m.%Y',
 63		'datetime' => '%d.%m.%Y %H:%M:%S',
 64		'time' => '%H:%M:%S',
 65		'firstday' => 0);
 66
 67	/**
 68	 * get an L10N instance
 69	 * @return OC_L10N
 70	 */
 71	public static function get($app, $lang=null) {
 72		if(is_null($lang)) {
 73			if(!isset(self::$instances[$app])) {
 74				self::$instances[$app]=new OC_L10N($app);
 75			}
 76			return self::$instances[$app];
 77		}else{
 78			return new OC_L10N($app, $lang);
 79		}
 80	}
 81
 82	/**
 83	 * @brief The constructor
 84	 * @param $app the app requesting l10n
 85	 * @param $lang default: null Language
 86	 * @returns OC_L10N-Object
 87	 *
 88	 * If language is not set, the constructor tries to find the right
 89	 * language.
 90	 */
 91	public function __construct($app, $lang = null) {
 92		$this->app = $app;
 93		$this->lang = $lang;
 94	}
 95
 96	protected function init() {
 97		if ($this->app === true) {
 98			return;
 99		}
100		$app = OC_App::cleanAppId($this->app);
101		$lang = $this->lang;
102		$this->app = true;
103		// Find the right language
104		if(is_null($lang) || $lang == '') {
105			$lang = self::findLanguage($app);
106		}
107
108		// Use cache if possible
109		if(array_key_exists($app.'::'.$lang, self::$cache)) {
110
111			$this->translations = self::$cache[$app.'::'.$lang]['t'];
112			$this->localizations = self::$cache[$app.'::'.$lang]['l'];
113		}
114		else{
115			$i18ndir = self::findI18nDir($app);
116			// Localization is in /l10n, Texts are in $i18ndir
117			// (Just no need to define date/time format etc. twice)
118			if((OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC_App::getAppPath($app).'/l10n/')
119				|| OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/core/l10n/')
120				|| OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/lib/l10n/')
121				|| OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/settings')
122				)
123				&& file_exists($i18ndir.$lang.'.php')) {
124				// Include the file, save the data from $CONFIG
125				$transFile = strip_tags($i18ndir).strip_tags($lang).'.php';
126				include $transFile;
127				if(isset($TRANSLATIONS) && is_array($TRANSLATIONS)) {
128					$this->translations = $TRANSLATIONS;
129					//merge with translations from theme
130					$theme = OC_Config::getValue( "theme" );
131					if (!is_null($theme)) {
132						$transFile = OC::$SERVERROOT.'/themes/'.$theme.substr($transFile, strlen(OC::$SERVERROOT));
133						if (file_exists($transFile)) {
134							include $transFile;
135							if (isset($TRANSLATIONS) && is_array($TRANSLATIONS)) {
136								$this->translations = array_merge($this->translations, $TRANSLATIONS);
137							}
138						}
139					}
140				}
141			}
142
143			if(file_exists(OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php')) {
144				// Include the file, save the data from $CONFIG
145				include OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php';
146				if(isset($LOCALIZATIONS) && is_array($LOCALIZATIONS)) {
147					$this->localizations = array_merge($this->localizations, $LOCALIZATIONS);
148				}
149			}
150
151			self::$cache[$app.'::'.$lang]['t'] = $this->translations;
152			self::$cache[$app.'::'.$lang]['l'] = $this->localizations;
153		}
154	}
155
156	/**
157	 * @brief Translating
158	 * @param $text String The text we need a translation for
159	 * @param array $parameters default:array() Parameters for sprintf
160	 * @return \OC_L10N_String Translation or the same text
161	 *
162	 * Returns the translation. If no translation is found, $text will be
163	 * returned.
164	 */
165	public function t($text, $parameters = array()) {
166		return new OC_L10N_String($this, $text, $parameters);
167	}
168
169	/**
170	 * @brief Translating
171	 * @param $textArray The text array we need a translation for
172	 * @returns Translation or the same text
173	 *
174	 * Returns the translation. If no translation is found, $textArray will be
175	 * returned.
176	 *
177	 *
178	 * @deprecated deprecated since ownCloud version 5.0
179	 * This method will probably be removed with ownCloud 6.0
180	 *
181	 *
182	 */
183	public function tA($textArray) {
184		OC_Log::write('core', 'DEPRECATED: the method tA is deprecated and will be removed soon.', OC_Log::WARN);
185		$result = array();
186		foreach($textArray as $key => $text) {
187			$result[$key] = (string)$this->t($text);
188		}
189		return $result;
190	}
191
192	/**
193	 * @brief getTranslations
194	 * @returns Fetch all translations
195	 *
196	 * Returns an associative array with all translations
197	 */
198	public function getTranslations() {
199		$this->init();
200		return $this->translations;
201	}
202
203	/**
204	 * @brief Localization
205	 * @param $type Type of localization
206	 * @param $params parameters for this localization
207	 * @returns String or false
208	 *
209	 * Returns the localized data.
210	 *
211	 * Implemented types:
212	 *  - date
213	 *    - Creates a date
214	 *    - l10n-field: date
215	 *    - params: timestamp (int/string)
216	 *  - datetime
217	 *    - Creates date and time
218	 *    - l10n-field: datetime
219	 *    - params: timestamp (int/string)
220	 *  - time
221	 *    - Creates a time
222	 *    - l10n-field: time
223	 *    - params: timestamp (int/string)
224	 */
225	public function l($type, $data) {
226		$this->init();
227		switch($type) {
228			// If you add something don't forget to add it to $localizations
229			// at the top of the page
230			case 'date':
231			case 'datetime':
232			case 'time':
233				if($data instanceof DateTime) return $data->format($this->localizations[$type]);
234				elseif(is_string($data)) $data = strtotime($data);
235				$locales = array(self::findLanguage());
236				if (strlen($locales[0]) == 2) {
237					$locales[] = $locales[0].'_'.strtoupper($locales[0]);
238				}
239				setlocale(LC_TIME, $locales);
240				$format = $this->localizations[$type];
241				// Check for Windows to find and replace the %e modifier correctly
242				if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
243					$format = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $format);
244				}
245				return strftime($format, $data);
246				break;
247			case 'firstday':
248			case 'jsdate':
249				return $this->localizations[$type];
250			default:
251				return false;
252		}
253	}
254
255	/**
256	 * @brief Choose a language
257	 * @param $texts Associative Array with possible strings
258	 * @returns String
259	 *
260	 * $text is an array 'de' => 'hallo welt', 'en' => 'hello world', ...
261	 *
262	 * This function is useful to avoid loading thousands of files if only one
263	 * simple string is needed, for example in appinfo.php
264	 */
265	public static function selectLanguage($text) {
266		$lang = self::findLanguage(array_keys($text));
267		return $text[$lang];
268	}
269
270	/**
271	 * @brief find the best language
272	 * @param $app Array or string, details below
273	 * @returns language
274	 *
275	 * If $app is an array, ownCloud assumes that these are the available
276	 * languages. Otherwise ownCloud tries to find the files in the l10n
277	 * folder.
278	 *
279	 * If nothing works it returns 'en'
280	 */
281	public static function findLanguage($app = null) {
282		if(!is_array($app) && self::$language != '') {
283			return self::$language;
284		}
285
286		if(OC_User::getUser() && OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang')) {
287			$lang = OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang');
288			self::$language = $lang;
289			if(is_array($app)) {
290				$available = $app;
291				$lang_exists = array_search($lang, $available) !== false;
292			}
293			else {
294				$lang_exists = self::languageExists($app, $lang);
295			}
296			if($lang_exists) {
297				return $lang;
298			}
299		}
300
301		if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
302			$accepted_languages = preg_split('/,\s*/', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
303			if(is_array($app)) {
304				$available = $app;
305			}
306			else{
307				$available = self::findAvailableLanguages($app);
308			}
309			foreach($accepted_languages as $i) {
310				$temp = explode(';', $i);
311				$temp[0] = str_replace('-', '_', $temp[0]);
312				if( ($key = array_search($temp[0], $available)) !== false) {
313					if (is_null($app)) {
314						self::$language = $available[$key];
315					}
316					return $available[$key];
317				}
318				foreach($available as $l) {
319					if ( $temp[0] == substr($l, 0, 2) ) {
320						if (is_null($app)) {
321							self::$language = $l;
322						}
323						return $l;
324					}
325				}
326			}
327		}
328
329		// Last try: English
330		return 'en';
331	}
332
333	/**
334	 * @brief find the l10n directory
335	 * @param $app App that needs to be translated
336	 * @returns directory
337	 */
338	protected static function findI18nDir($app) {
339		// find the i18n dir
340		$i18ndir = OC::$SERVERROOT.'/core/l10n/';
341		if($app != '') {
342			// Check if the app is in the app folder
343			if(file_exists(OC_App::getAppPath($app).'/l10n/')) {
344				$i18ndir = OC_App::getAppPath($app).'/l10n/';
345			}
346			else{
347				$i18ndir = OC::$SERVERROOT.'/'.$app.'/l10n/';
348			}
349		}
350		return $i18ndir;
351	}
352
353	/**
354	 * @brief find all available languages for an app
355	 * @param $app App that needs to be translated
356	 * @returns array an array of available languages
357	 */
358	public static function findAvailableLanguages($app=null) {
359		$available=array('en');//english is always available
360		$dir = self::findI18nDir($app);
361		if(is_dir($dir)) {
362			$files=scandir($dir);
363			foreach($files as $file) {
364				if(substr($file, -4, 4) === '.php' && substr($file, 0, 4) !== 'l10n') {
365					$i = substr($file, 0, -4);
366					$available[] = $i;
367				}
368			}
369		}
370		return $available;
371	}
372
373	public static function languageExists($app, $lang) {
374		if ($lang == 'en') {//english is always available
375			return true;
376		}
377		$dir = self::findI18nDir($app);
378		if(is_dir($dir)) {
379			return file_exists($dir.'/'.$lang.'.php');
380		}
381		return false;
382	}
383}