]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions-l10n.php
Use __DIR__ instead of dirname(__FILE__)
[Github/YOURLS.git] / includes / functions-l10n.php
1 <?php
2 /**
3  * YOURLS Translation API
4  *
5  * YOURLS modification of a small subset from WordPress' Translation API implementation.
6  * GPL License
7  *
8  * @package POMO
9  * @subpackage i18n
10  */
11
12 /**
13  * Load POMO files required to run library
14  */
15 require_once __DIR__ . '/pomo/mo.php';
16 require_once __DIR__ . '/pomo/translations.php';
17
18 /**
19  * Gets the current locale.
20  *
21  * If the locale is set, then it will filter the locale in the 'get_locale' filter
22  * hook and return the value.
23  *
24  * If the locale is not set already, then the YOURLS_LANG constant is used if it is
25  * defined. Then it is filtered through the 'get_locale' filter hook and the value
26  * for the locale global set and the locale is returned.
27  *
28  * The process to get the locale should only be done once, but the locale will
29  * always be filtered using the 'get_locale' hook.
30  *
31  * @since 1.6
32  * @uses yourls_apply_filter() Calls 'get_locale' hook on locale value.
33  * @uses $yourls_locale Gets the locale stored in the global.
34  *
35  * @return string The locale of the blog or from the 'get_locale' hook.
36  */
37 function yourls_get_locale() {
38         global $yourls_locale;
39
40         if ( !isset( $yourls_locale ) ) {
41                 // YOURLS_LANG is defined in config.
42                 if ( defined( 'YOURLS_LANG' ) )
43                         $yourls_locale = YOURLS_LANG;
44         }
45
46     if ( !$yourls_locale )
47         $yourls_locale = '';
48
49         return yourls_apply_filter( 'get_locale', $yourls_locale );
50 }
51
52 /**
53  * Retrieves the translation of $text. If there is no translation, or
54  * the domain isn't loaded, the original text is returned.
55  *
56  * @see yourls__() Don't use yourls_translate() directly, use yourls__()
57  * @since 1.6
58  * @uses yourls_apply_filter() Calls 'translate' on domain translated text
59  *              with the untranslated text as second parameter.
60  *
61  * @param string $text Text to translate.
62  * @param string $domain Domain to retrieve the translated text.
63  * @return string Translated text
64  */
65 function yourls_translate( $text, $domain = 'default' ) {
66         $translations = yourls_get_translations_for_domain( $domain );
67         return yourls_apply_filter( 'translate', $translations->translate( $text ), $text, $domain );
68 }
69
70 /**
71  * Retrieves the translation of $text with a given $context. If there is no translation, or
72  * the domain isn't loaded, the original text is returned.
73  *
74  * Quite a few times, there will be collisions with similar translatable text
75  * found in more than two places but with different translated context.
76  *
77  * By including the context in the pot file translators can translate the two
78  * strings differently.
79  *
80  * @since 1.6
81  * @param string $text Text to translate.
82  * @param string $context Context.
83  * @param string $domain Domain to retrieve the translated text.
84  * @return string Translated text
85  */
86 function yourls_translate_with_context( $text, $context, $domain = 'default' ) {
87         $translations = yourls_get_translations_for_domain( $domain );
88         return yourls_apply_filter( 'translate_with_context', $translations->translate( $text, $context ), $text, $context, $domain );
89 }
90
91 /**
92  * Retrieves the translation of $text. If there is no translation, or
93  * the domain isn't loaded, the original text is returned.
94  *
95  * @see yourls_translate() An alias of yourls_translate()
96  * @since 1.6
97  *
98  * @param string $text Text to translate
99  * @param string $domain Optional. Domain to retrieve the translated text
100  * @return string Translated text
101  */
102 function yourls__( $text, $domain = 'default' ) {
103         return yourls_translate( $text, $domain );
104 }
105
106 /**
107  * Return a translated sprintf() string (mix yourls__() and sprintf() in one func)
108  *
109  * Instead of doing sprintf( yourls__( 'string %s' ), $arg ) you can simply use:
110  * yourls_s( 'string %s', $arg )
111  * This function accepts an arbitrary number of arguments:
112  * - first one will be the string to translate, eg "hello %s my name is %s"
113  * - following ones will be the sprintf arguments, eg "world" and "Ozh"
114  * - if there are more arguments passed than needed, the last one will be used as the translation domain
115  *
116  * @see sprintf()
117  * @since 1.6
118  *
119  * @param string $pattern Text to translate
120  * @param string $arg1, $arg2... Optional: sprintf tokens, and translation domain
121  * @return string Translated text
122  */
123 function yourls_s( $pattern ) {
124         // Get pattern and pattern arguments 
125         $args = func_get_args();
126         // If yourls_s() called by yourls_se(), all arguments are wrapped in the same array key
127         if( count( $args ) == 1 && is_array( $args[0] ) ) {
128                 $args = $args[0];
129         }
130         $pattern = $args[0];
131         
132         // get list of sprintf tokens (%s and such)
133         $num_of_tokens = substr_count( $pattern, '%' ) - 2 * substr_count( $pattern, '%%' );
134         
135         $domain = 'default';
136         // More arguments passed than needed for the sprintf? The last one will be the domain
137         if( $num_of_tokens < ( count( $args ) - 1 ) ) {
138                 $domain = array_pop( $args );
139         }
140         
141         // Translate text
142         $args[0] = yourls__( $pattern, $domain );
143         
144         return call_user_func_array( 'sprintf', $args );        
145 }
146
147 /**
148  * Echo a translated sprintf() string (mix yourls__() and sprintf() in one func)
149  *
150  * Instead of doing printf( yourls__( 'string %s' ), $arg ) you can simply use:
151  * yourls_se( 'string %s', $arg )
152  * This function accepts an arbitrary number of arguments:
153  * - first one will be the string to translate, eg "hello %s my name is %s"
154  * - following ones will be the sprintf arguments, eg "world" and "Ozh"
155  * - if there are more arguments passed than needed, the last one will be used as the translation domain
156  *
157  * @see yourls_s()
158  * @see sprintf()
159  * @since 1.6
160  *
161  * @param string $text Text to translate
162  * @param string $arg1, $arg2... Optional: sprintf tokens, and translation domain
163  * @return string Translated text
164  */
165 function yourls_se( $pattern ) {
166         echo yourls_s( func_get_args() );
167 }
168
169
170 /**
171  * Retrieves the translation of $text and escapes it for safe use in an attribute.
172  * If there is no translation, or the domain isn't loaded, the original text is returned.
173  *
174  * @see yourls_translate() An alias of yourls_translate()
175  * @see yourls_esc_attr()
176  * @since 1.6
177  *
178  * @param string $text Text to translate
179  * @param string $domain Optional. Domain to retrieve the translated text
180  * @return string Translated text
181  */
182 function yourls_esc_attr__( $text, $domain = 'default' ) {
183         return yourls_esc_attr( yourls_translate( $text, $domain ) );
184 }
185
186 /**
187  * Retrieves the translation of $text and escapes it for safe use in HTML output.
188  * If there is no translation, or the domain isn't loaded, the original text is returned.
189  *
190  * @see yourls_translate() An alias of yourls_translate()
191  * @see yourls_esc_html()
192  * @since 1.6
193  *
194  * @param string $text Text to translate
195  * @param string $domain Optional. Domain to retrieve the translated text
196  * @return string Translated text
197  */
198 function yourls_esc_html__( $text, $domain = 'default' ) {
199         return yourls_esc_html( yourls_translate( $text, $domain ) );
200 }
201
202 /**
203  * Displays the returned translated text from yourls_translate().
204  *
205  * @see yourls_translate() Echoes returned yourls_translate() string
206  * @since 1.6
207  *
208  * @param string $text Text to translate
209  * @param string $domain Optional. Domain to retrieve the translated text
210  */
211 function yourls_e( $text, $domain = 'default' ) {
212         echo yourls_translate( $text, $domain );
213 }
214
215 /**
216  * Displays translated text that has been escaped for safe use in an attribute.
217  *
218  * @see yourls_translate() Echoes returned yourls_translate() string
219  * @see yourls_esc_attr()
220  * @since 1.6
221  *
222  * @param string $text Text to translate
223  * @param string $domain Optional. Domain to retrieve the translated text
224  */
225 function yourls_esc_attr_e( $text, $domain = 'default' ) {
226         echo yourls_esc_attr( yourls_translate( $text, $domain ) );
227 }
228
229 /**
230  * Displays translated text that has been escaped for safe use in HTML output.
231  *
232  * @see yourls_translate() Echoes returned yourls_translate() string
233  * @see yourls_esc_html()
234  * @since 1.6
235  *
236  * @param string $text Text to translate
237  * @param string $domain Optional. Domain to retrieve the translated text
238  */
239 function yourls_esc_html_e( $text, $domain = 'default' ) {
240         echo yourls_esc_html( yourls_translate( $text, $domain ) );
241 }
242
243 /**
244  * Retrieve translated string with gettext context
245  *
246  * Quite a few times, there will be collisions with similar translatable text
247  * found in more than two places but with different translated context.
248  *
249  * By including the context in the pot file translators can translate the two
250  * strings differently.
251  *
252  * @since 1.6
253  *
254  * @param string $text Text to translate
255  * @param string $context Context information for the translators
256  * @param string $domain Optional. Domain to retrieve the translated text
257  * @return string Translated context string
258  */
259 function yourls_x( $text, $context, $domain = 'default' ) {
260         return yourls_translate_with_context( $text, $context, $domain );
261 }
262
263 /**
264  * Displays translated string with gettext context
265  *
266  * @see yourls_x()
267  * @since 1.7.1
268  *
269  * @param string $text Text to translate
270  * @param string $context Context information for the translators
271  * @param string $domain Optional. Domain to retrieve the translated text
272  * @return string Translated context string
273  */
274 function yourls_xe( $text, $context, $domain = 'default' ) {
275         echo yourls_x( $text, $context, $domain );
276 }
277
278
279 /**
280  * Return translated text, with context, that has been escaped for safe use in an attribute
281  *
282  * @see yourls_translate() Return returned yourls_translate() string
283  * @see yourls_esc_attr()
284  * @see yourls_x()
285  * @since 1.6
286  *
287  * @param string   $single
288  * @param string   $context
289  * @param string   $domain Optional. Domain to retrieve the translated text
290  * @internal param string $text Text to translate
291  * @return string
292  */
293 function yourls_esc_attr_x( $single, $context, $domain = 'default' ) {
294         return yourls_esc_attr( yourls_translate_with_context( $single, $context, $domain ) );
295 }
296
297 /**
298  * Return translated text, with context, that has been escaped for safe use in HTML output
299  *
300  * @see yourls_translate() Return returned yourls_translate() string
301  * @see yourls_esc_attr()
302  * @see yourls_x()
303  * @since 1.6
304  *
305  * @param string   $single
306  * @param string   $context
307  * @param string   $domain Optional. Domain to retrieve the translated text
308  * @internal param string $text Text to translate
309  * @return string
310  */
311 function yourls_esc_html_x( $single, $context, $domain = 'default' ) {
312         return yourls_esc_html( yourls_translate_with_context( $single, $context, $domain ) );
313 }
314
315 /**
316  * Retrieve the plural or single form based on the amount.
317  *
318  * If the domain is not set in the $yourls_l10n list, then a comparison will be made
319  * and either $plural or $single parameters returned.
320  *
321  * If the domain does exist, then the parameters $single, $plural, and $number
322  * will first be passed to the domain's ngettext method. Then it will be passed
323  * to the 'translate_n' filter hook along with the same parameters. The expected
324  * type will be a string.
325  *
326  * @since 1.6
327  * @uses $yourls_l10n Gets list of domain translated string (gettext_reader) objects
328  * @uses yourls_apply_filter() Calls 'translate_n' hook on domains text returned,
329  *              along with $single, $plural, and $number parameters. Expected to return string.
330  *
331  * @param string $single The text that will be used if $number is 1
332  * @param string $plural The text that will be used if $number is not 1
333  * @param int $number The number to compare against to use either $single or $plural
334  * @param string $domain Optional. The domain identifier the text should be retrieved in
335  * @return string Either $single or $plural translated text
336  */
337 function yourls_n( $single, $plural, $number, $domain = 'default' ) {
338         $translations = yourls_get_translations_for_domain( $domain );
339         $translation = $translations->translate_plural( $single, $plural, $number );
340         return yourls_apply_filter( 'translate_n', $translation, $single, $plural, $number, $domain );
341 }
342
343 /**
344  * A hybrid of yourls_n() and yourls_x(). It supports contexts and plurals.
345  *
346  * @since 1.6
347  * @see yourls_n()
348  * @see yourls_x()
349  *
350  */
351 function yourls_nx($single, $plural, $number, $context, $domain = 'default') {
352         $translations = yourls_get_translations_for_domain( $domain );
353         $translation = $translations->translate_plural( $single, $plural, $number, $context );
354         return yourls_apply_filter( 'translate_nx', $translation, $single, $plural, $number, $context, $domain );
355 }
356
357 /**
358  * Register plural strings in POT file, but don't translate them.
359  *
360  * Used when you want to keep structures with translatable plural strings and
361  * use them later.
362  *
363  * Example:
364  *  $messages = array(
365  *      'post' => yourls_n_noop('%s post', '%s posts'),
366  *      'page' => yourls_n_noop('%s pages', '%s pages')
367  *  );
368  *  ...
369  *  $message = $messages[$type];
370  *  $usable_text = sprintf( yourls_translate_nooped_plural( $message, $count ), $count );
371  *
372  * @since 1.6
373  * @param string $singular Single form to be i18ned
374  * @param string $plural Plural form to be i18ned
375  * @param string $domain Optional. The domain identifier the text will be retrieved in
376  * @return array array($singular, $plural)
377  */
378 function yourls_n_noop( $singular, $plural, $domain = null ) {
379         return array(
380                 0 => $singular,
381                 1 => $plural, 
382                 'singular' => $singular,
383                 'plural' => $plural,
384                 'context' => null,
385                 'domain' => $domain
386         );
387 }
388
389 /**
390  * Register plural strings with context in POT file, but don't translate them.
391  *
392  * @since 1.6
393  * @see yourls_n_noop()
394  */
395 function yourls_nx_noop( $singular, $plural, $context, $domain = null ) {
396         return array(
397                 0 => $singular,
398                 1 => $plural,
399                 2 => $context,
400                 'singular' => $singular,
401                 'plural' => $plural,
402                 'context' => $context,
403                 'domain' => $domain
404         );
405 }
406
407 /**
408  * Translate the result of yourls_n_noop() or yourls_nx_noop()
409  *
410  * @since 1.6
411  * @param array $nooped_plural Array with singular, plural and context keys, usually the result of yourls_n_noop() or yourls_nx_noop()
412  * @param int $count Number of objects
413  * @param string $domain Optional. The domain identifier the text should be retrieved in. If $nooped_plural contains
414  *      a domain passed to yourls_n_noop() or yourls_nx_noop(), it will override this value.
415  * @return string
416  */
417 function yourls_translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) {
418         if ( $nooped_plural['domain'] )
419                 $domain = $nooped_plural['domain'];
420
421         if ( $nooped_plural['context'] )
422                 return yourls_nx( $nooped_plural['singular'], $nooped_plural['plural'], $count, $nooped_plural['context'], $domain );
423         else
424                 return yourls_n( $nooped_plural['singular'], $nooped_plural['plural'], $count, $domain );
425 }
426
427 /**
428  * Loads a MO file into the domain $domain.
429  *
430  * If the domain already exists, the translations will be merged. If both
431  * sets have the same string, the translation from the original value will be taken.
432  *
433  * On success, the .mo file will be placed in the $yourls_l10n global by $domain
434  * and will be a MO object.
435  *
436  * @since 1.6
437  * @uses $yourls_l10n Gets list of domain translated string objects
438  *
439  * @param string $domain Unique identifier for retrieving translated strings
440  * @param string $mofile Path to the .mo file
441  * @return bool True on success, false on failure
442  */
443 function yourls_load_textdomain( $domain, $mofile ) {
444         global $yourls_l10n;
445
446         $plugin_override = yourls_apply_filter( 'override_load_textdomain', false, $domain, $mofile );
447
448         if ( true == $plugin_override ) {
449                 return true;
450         }
451
452         yourls_do_action( 'load_textdomain', $domain, $mofile );
453
454         $mofile = yourls_apply_filter( 'load_textdomain_mofile', $mofile, $domain );
455
456         if ( !is_readable( $mofile ) ) {
457         trigger_error( 'Cannot read file ' . str_replace( YOURLS_ABSPATH.'/', '', $mofile ) . '.'
458                     . ' Make sure there is a language file installed. More info: http://yourls.org/translations' );
459         return false;
460     }
461
462         $mo = new MO();
463         if ( !$mo->import_from_file( $mofile ) )
464         return false;
465
466         if ( isset( $yourls_l10n[$domain] ) )
467                 $mo->merge_with( $yourls_l10n[$domain] );
468
469         $yourls_l10n[$domain] = &$mo;
470
471         return true;
472 }
473
474 /**
475  * Unloads translations for a domain
476  *
477  * @since 1.6
478  * @param string $domain Textdomain to be unloaded
479  * @return bool Whether textdomain was unloaded
480  */
481 function yourls_unload_textdomain( $domain ) {
482         global $yourls_l10n;
483
484         $plugin_override = yourls_apply_filter( 'override_unload_textdomain', false, $domain );
485
486         if ( $plugin_override )
487                 return true;
488
489         yourls_do_action( 'unload_textdomain', $domain );
490
491         if ( isset( $yourls_l10n[$domain] ) ) {
492                 unset( $yourls_l10n[$domain] );
493                 return true;
494         }
495
496         return false;
497 }
498
499 /**
500  * Loads default translated strings based on locale.
501  *
502  * Loads the .mo file in YOURLS_LANG_DIR constant path from YOURLS root. The
503  * translated (.mo) file is named based on the locale.
504  *
505  * @since 1.6
506  * @return bool True on success, false on failure
507  */
508 function yourls_load_default_textdomain() {
509         $yourls_locale = yourls_get_locale();
510     
511     if( !empty( $yourls_locale ) )
512         return yourls_load_textdomain( 'default', YOURLS_LANG_DIR . "/$yourls_locale.mo" );
513 }
514
515 /**
516  * Returns the Translations instance for a domain. If there isn't one,
517  * returns empty Translations instance.
518  *
519  * @param string $domain
520  * @return object A Translation instance
521  */
522 function yourls_get_translations_for_domain( $domain ) {
523         global $yourls_l10n;
524         if ( !isset( $yourls_l10n[$domain] ) ) {
525                 $yourls_l10n[$domain] = new NOOP_Translations;
526         }
527         return $yourls_l10n[$domain];
528 }
529
530 /**
531  * Whether there are translations for the domain
532  *
533  * @since 1.6
534  * @param string $domain
535  * @return bool Whether there are translations
536  */
537 function yourls_is_textdomain_loaded( $domain ) {
538         global $yourls_l10n;
539         return isset( $yourls_l10n[$domain] );
540 }
541
542 /**
543  * Translates role name. Unused.
544  *
545  * Unused function for the moment, we'll see when there are roles.
546  * From the WP source: Since the role names are in the database and
547  * not in the source there are dummy gettext calls to get them into the POT
548  * file and this function properly translates them back.
549  *
550  * @since 1.6
551  */
552 function yourls_translate_user_role( $name ) {
553         return yourls_translate_with_context( $name, 'User role' );
554 }
555
556 /**
557  * Get all available languages (*.mo files) in a given directory. The default directory is YOURLS_LANG_DIR.
558  *
559  * @since 1.6
560  *
561  * @param string $dir A directory in which to search for language files. The default directory is YOURLS_LANG_DIR.
562  * @return array Array of language codes or an empty array if no languages are present. Language codes are formed by stripping the .mo extension from the language file names.
563  */
564 function yourls_get_available_languages( $dir = null ) {
565         $languages = array();
566         
567         $dir = is_null( $dir) ? YOURLS_LANG_DIR : $dir;
568         
569         foreach( (array) glob( $dir . '/*.mo' ) as $lang_file ) {
570                 $languages[] = basename( $lang_file, '.mo' );
571         }
572         
573         return yourls_apply_filter( 'get_available_languages', $languages );
574 }
575
576 /**
577  * Return integer number to format based on the locale.
578  *
579  * @since 1.6
580  *
581  * @param int $number The number to convert based on locale.
582  * @param int $decimals Precision of the number of decimal places.
583  * @return string Converted number in string format.
584  */
585 function yourls_number_format_i18n( $number, $decimals = 0 ) {
586         global $yourls_locale_formats;
587         if( !isset( $yourls_locale_formats ) )
588                 $yourls_locale_formats = new YOURLS_Locale_Formats();
589                 
590         $formatted = number_format( $number, abs( intval( $decimals ) ), $yourls_locale_formats->number_format['decimal_point'], $yourls_locale_formats->number_format['thousands_sep'] );
591         return yourls_apply_filter( 'number_format_i18n', $formatted );
592 }
593
594 /**
595  * Return the date in localized format, based on timestamp.
596  *
597  * If the locale specifies the locale month and weekday, then the locale will
598  * take over the format for the date. If it isn't, then the date format string
599  * will be used instead.
600  *
601  * @since 1.6
602  *
603  * @param string   $dateformatstring Format to display the date.
604  * @param bool|int $unixtimestamp    Optional. Unix timestamp.
605  * @param bool     $gmt              Optional, default is false. Whether to convert to GMT for time.
606  * @return string The date, translated if locale specifies it.
607  */
608 function yourls_date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = false ) {
609         global $yourls_locale_formats;
610         if( !isset( $yourls_locale_formats ) )
611                 $yourls_locale_formats = new YOURLS_Locale_Formats();
612
613         $i = $unixtimestamp;
614
615         if ( false === $i ) {
616                 if ( ! $gmt )
617                         $i = yourls_current_time( 'timestamp' );
618                 else
619                         $i = time();
620                 // we should not let date() interfere with our
621                 // specially computed timestamp
622                 $gmt = true;
623         }
624
625         // store original value for language with untypical grammars
626         // see http://core.trac.wordpress.org/ticket/9396
627         $req_format = $dateformatstring;
628
629         $datefunc = $gmt? 'gmdate' : 'date';
630
631         if ( ( !empty( $yourls_locale_formats->month ) ) && ( !empty( $yourls_locale_formats->weekday ) ) ) {
632                 $datemonth            = $yourls_locale_formats->get_month( $datefunc( 'm', $i ) );
633                 $datemonth_abbrev     = $yourls_locale_formats->get_month_abbrev( $datemonth );
634                 $dateweekday          = $yourls_locale_formats->get_weekday( $datefunc( 'w', $i ) );
635                 $dateweekday_abbrev   = $yourls_locale_formats->get_weekday_abbrev( $dateweekday );
636                 $datemeridiem         = $yourls_locale_formats->get_meridiem( $datefunc( 'a', $i ) );
637                 $datemeridiem_capital = $yourls_locale_formats->get_meridiem( $datefunc( 'A', $i ) );
638                 
639                 $dateformatstring = ' '.$dateformatstring;
640                 $dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . yourls_backslashit( $dateweekday_abbrev ), $dateformatstring );
641                 $dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . yourls_backslashit( $datemonth ), $dateformatstring );
642                 $dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . yourls_backslashit( $dateweekday ), $dateformatstring );
643                 $dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . yourls_backslashit( $datemonth_abbrev ), $dateformatstring );
644                 $dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . yourls_backslashit( $datemeridiem ), $dateformatstring );
645                 $dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . yourls_backslashit( $datemeridiem_capital ), $dateformatstring );
646
647                 $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
648         }
649         $timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
650         $timezone_formats_re = implode( '|', $timezone_formats );
651         if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
652         
653                 // TODO: implement a timezone option
654                 $timezone_string = yourls_get_option( 'timezone_string' );
655                 if ( $timezone_string ) {
656                         $timezone_object = timezone_open( $timezone_string );
657                         $date_object = date_create( null, $timezone_object );
658                         foreach( $timezone_formats as $timezone_format ) {
659                                 if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
660                                         $formatted = date_format( $date_object, $timezone_format );
661                                         $dateformatstring = ' '.$dateformatstring;
662                                         $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . yourls_backslashit( $formatted ), $dateformatstring );
663                                         $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
664                                 }
665                         }
666                 }
667         }
668         $j = @$datefunc( $dateformatstring, $i );
669         // allow plugins to redo this entirely for languages with untypical grammars
670         $j = yourls_apply_filter('date_i18n', $j, $req_format, $i, $gmt);
671         return $j;
672 }
673
674 /**
675  * Retrieve the current time based on specified type. Stolen from WP.
676  *
677  * The 'mysql' type will return the time in the format for MySQL DATETIME field.
678  * The 'timestamp' type will return the current timestamp.
679  *
680  * If $gmt is set to either '1' or 'true', then both types will use GMT time.
681  * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
682  *
683  * @since 1.6
684  *
685  * @param string $type Either 'mysql' or 'timestamp'.
686  * @param int|bool $gmt Optional. Whether to use GMT timezone. Default is false.
687  * @return int|string String if $type is 'gmt', int if $type is 'timestamp'.
688  */
689 function yourls_current_time( $type, $gmt = 0 ) {
690         switch ( $type ) {
691                 case 'mysql':
692                         return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', time() + YOURLS_HOURS_OFFSET * 3600 );
693                         break;
694                 case 'timestamp':
695                         return ( $gmt ) ? time() : time() + YOURLS_HOURS_OFFSET * 3600;
696                         break;
697         }
698 }
699
700
701 /**
702  * Class that loads the calendar locale.
703  *
704  * @since 1.6
705  */
706 class YOURLS_Locale_Formats {
707         /**
708          * Stores the translated strings for the full weekday names.
709          *
710          * @since 1.6
711          * @var array
712          * @access private
713          */
714         var $weekday;
715
716         /**
717          * Stores the translated strings for the one character weekday names.
718          *
719          * There is a hack to make sure that Tuesday and Thursday, as well
720          * as Sunday and Saturday, don't conflict. See init() method for more.
721          *
722          * @see YOURLS_Locale_Formats::init() for how to handle the hack.
723          *
724          * @since 1.6
725          * @var array
726          * @access private
727          */
728         var $weekday_initial;
729
730         /**
731          * Stores the translated strings for the abbreviated weekday names.
732          *
733          * @since 1.6
734          * @var array
735          * @access private
736          */
737         var $weekday_abbrev;
738
739         /**
740          * Stores the translated strings for the full month names.
741          *
742          * @since 1.6
743          * @var array
744          * @access private
745          */
746         var $month;
747
748         /**
749          * Stores the translated strings for the abbreviated month names.
750          *
751          * @since 1.6
752          * @var array
753          * @access private
754          */
755         var $month_abbrev;
756
757         /**
758          * Stores the translated strings for 'am' and 'pm'.
759          *
760          * Also the capitalized versions.
761          *
762          * @since 1.6
763          * @var array
764          * @access private
765          */
766         var $meridiem;
767
768         /**
769          * Stores the translated number format
770          *
771          * @since 1.6
772          * @var array
773          * @access private
774          */
775         var $number_format;
776
777         /**
778          * The text direction of the locale language.
779          *
780          * Default is left to right 'ltr'.
781          *
782          * @since 1.6
783          * @var string
784          * @access private
785          */
786         var $text_direction = 'ltr';
787
788         /**
789          * Sets up the translated strings and object properties.
790          *
791          * The method creates the translatable strings for various
792          * calendar elements. Which allows for specifying locale
793          * specific calendar names and text direction.
794          *
795          * @since 1.6
796          * @access private
797          */
798         function init() {
799                 // The Weekdays
800                 $this->weekday[0] = /* //translators: weekday */ yourls__( 'Sunday' );
801                 $this->weekday[1] = /* //translators: weekday */ yourls__( 'Monday' );
802                 $this->weekday[2] = /* //translators: weekday */ yourls__( 'Tuesday' );
803                 $this->weekday[3] = /* //translators: weekday */ yourls__( 'Wednesday' );
804                 $this->weekday[4] = /* //translators: weekday */ yourls__( 'Thursday' );
805                 $this->weekday[5] = /* //translators: weekday */ yourls__( 'Friday' );
806                 $this->weekday[6] = /* //translators: weekday */ yourls__( 'Saturday' );
807
808                 // The first letter of each day. The _%day%_initial suffix is a hack to make
809                 // sure the day initials are unique.
810                 $this->weekday_initial[yourls__( 'Sunday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'S_Sunday_initial' );
811                 $this->weekday_initial[yourls__( 'Monday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'M_Monday_initial' );
812                 $this->weekday_initial[yourls__( 'Tuesday' )]   = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'T_Tuesday_initial' );
813                 $this->weekday_initial[yourls__( 'Wednesday' )] = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'W_Wednesday_initial' );
814                 $this->weekday_initial[yourls__( 'Thursday' )]  = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'T_Thursday_initial' );
815                 $this->weekday_initial[yourls__( 'Friday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'F_Friday_initial' );
816                 $this->weekday_initial[yourls__( 'Saturday' )]  = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'S_Saturday_initial' );
817
818                 foreach ($this->weekday_initial as $weekday_ => $weekday_initial_) {
819                         $this->weekday_initial[$weekday_] = preg_replace('/_.+_initial$/', '', $weekday_initial_);
820                 }
821
822                 // Abbreviations for each day.
823                 $this->weekday_abbrev[ yourls__( 'Sunday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Sun' );
824                 $this->weekday_abbrev[ yourls__( 'Monday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Mon' );
825                 $this->weekday_abbrev[ yourls__( 'Tuesday' ) ]   = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Tue' );
826                 $this->weekday_abbrev[ yourls__( 'Wednesday' ) ] = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Wed' );
827                 $this->weekday_abbrev[ yourls__( 'Thursday' ) ]  = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Thu' );
828                 $this->weekday_abbrev[ yourls__( 'Friday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Fri' );
829                 $this->weekday_abbrev[ yourls__( 'Saturday' ) ]  = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Sat' );
830
831                 // The Months
832                 $this->month['01'] = /* //translators: month name */ yourls__( 'January' );
833                 $this->month['02'] = /* //translators: month name */ yourls__( 'February' );
834                 $this->month['03'] = /* //translators: month name */ yourls__( 'March' );
835                 $this->month['04'] = /* //translators: month name */ yourls__( 'April' );
836                 $this->month['05'] = /* //translators: month name */ yourls__( 'May' );
837                 $this->month['06'] = /* //translators: month name */ yourls__( 'June' );
838                 $this->month['07'] = /* //translators: month name */ yourls__( 'July' );
839                 $this->month['08'] = /* //translators: month name */ yourls__( 'August' );
840                 $this->month['09'] = /* //translators: month name */ yourls__( 'September' );
841                 $this->month['10'] = /* //translators: month name */ yourls__( 'October' );
842                 $this->month['11'] = /* //translators: month name */ yourls__( 'November' );
843                 $this->month['12'] = /* //translators: month name */ yourls__( 'December' );
844
845                 // Abbreviations for each month. Uses the same hack as above to get around the
846                 // 'May' duplication.
847                 $this->month_abbrev[ yourls__( 'January' ) ]   = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jan_January_abbreviation' );
848                 $this->month_abbrev[ yourls__( 'February' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Feb_February_abbreviation' );
849                 $this->month_abbrev[ yourls__( 'March' ) ]     = /* //translators: three-letter abbreviation of the month */ yourls__( 'Mar_March_abbreviation' );
850                 $this->month_abbrev[ yourls__( 'April' ) ]     = /* //translators: three-letter abbreviation of the month */ yourls__( 'Apr_April_abbreviation' );
851                 $this->month_abbrev[ yourls__( 'May' ) ]       = /* //translators: three-letter abbreviation of the month */ yourls__( 'May_May_abbreviation' );
852                 $this->month_abbrev[ yourls__( 'June' ) ]      = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jun_June_abbreviation' );
853                 $this->month_abbrev[ yourls__( 'July' ) ]      = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jul_July_abbreviation' );
854                 $this->month_abbrev[ yourls__( 'August' ) ]    = /* //translators: three-letter abbreviation of the month */ yourls__( 'Aug_August_abbreviation' );
855                 $this->month_abbrev[ yourls__( 'September' ) ] = /* //translators: three-letter abbreviation of the month */ yourls__( 'Sep_September_abbreviation' );
856                 $this->month_abbrev[ yourls__( 'October' ) ]   = /* //translators: three-letter abbreviation of the month */ yourls__( 'Oct_October_abbreviation' );
857                 $this->month_abbrev[ yourls__( 'November' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Nov_November_abbreviation' );
858                 $this->month_abbrev[ yourls__( 'December' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Dec_December_abbreviation' );
859
860                 foreach ($this->month_abbrev as $month_ => $month_abbrev_) {
861                         $this->month_abbrev[$month_] = preg_replace('/_.+_abbreviation$/', '', $month_abbrev_);
862                 }
863
864                 // The Meridiems
865                 $this->meridiem['am'] = yourls__( 'am' );
866                 $this->meridiem['pm'] = yourls__( 'pm' );
867                 $this->meridiem['AM'] = yourls__( 'AM' );
868                 $this->meridiem['PM'] = yourls__( 'PM' );
869
870                 // Numbers formatting
871                 // See http://php.net/number_format
872
873                 /* //translators: $thousands_sep argument for http://php.net/number_format, default is , */
874                 $trans = yourls__( 'number_format_thousands_sep' );
875                 $this->number_format['thousands_sep'] = ('number_format_thousands_sep' == $trans) ? ',' : $trans;
876
877                 /* //translators: $dec_point argument for http://php.net/number_format, default is . */
878                 $trans = yourls__( 'number_format_decimal_point' );
879                 $this->number_format['decimal_point'] = ('number_format_decimal_point' == $trans) ? '.' : $trans;
880
881                 // Set text direction.
882                 if ( isset( $GLOBALS['text_direction'] ) )
883                         $this->text_direction = $GLOBALS['text_direction'];
884                 /* //translators: 'rtl' or 'ltr'. This sets the text direction for YOURLS. */
885                 elseif ( 'rtl' == yourls_x( 'ltr', 'text direction' ) )
886                         $this->text_direction = 'rtl';
887         }
888
889         /**
890          * Retrieve the full translated weekday word.
891          *
892          * Week starts on translated Sunday and can be fetched
893          * by using 0 (zero). So the week starts with 0 (zero)
894          * and ends on Saturday with is fetched by using 6 (six).
895          *
896          * @since 1.6
897          * @access public
898          *
899          * @param int $weekday_number 0 for Sunday through 6 Saturday
900          * @return string Full translated weekday
901          */
902         function get_weekday( $weekday_number ) {
903                 return $this->weekday[ $weekday_number ];
904         }
905
906         /**
907          * Retrieve the translated weekday initial.
908          *
909          * The weekday initial is retrieved by the translated
910          * full weekday word. When translating the weekday initial
911          * pay attention to make sure that the starting letter does
912          * not conflict.
913          *
914          * @since 1.6
915          * @access public
916          *
917          * @param string $weekday_name
918          * @return string
919          */
920         function get_weekday_initial( $weekday_name ) {
921                 return $this->weekday_initial[ $weekday_name ];
922         }
923
924         /**
925          * Retrieve the translated weekday abbreviation.
926          *
927          * The weekday abbreviation is retrieved by the translated
928          * full weekday word.
929          *
930          * @since 1.6
931          * @access public
932          *
933          * @param string $weekday_name Full translated weekday word
934          * @return string Translated weekday abbreviation
935          */
936         function get_weekday_abbrev( $weekday_name ) {
937                 return $this->weekday_abbrev[ $weekday_name ];
938         }
939
940         /**
941          * Retrieve the full translated month by month number.
942          *
943          * The $month_number parameter has to be a string
944          * because it must have the '0' in front of any number
945          * that is less than 10. Starts from '01' and ends at
946          * '12'.
947          *
948          * You can use an integer instead and it will add the
949          * '0' before the numbers less than 10 for you.
950          *
951          * @since 1.6
952          * @access public
953          *
954          * @param string|int $month_number '01' through '12'
955          * @return string Translated full month name
956          */
957         function get_month( $month_number ) {
958                 return $this->month[ sprintf( '%02s', $month_number ) ];                
959         }
960
961         /**
962          * Retrieve translated version of month abbreviation string.
963          *
964          * The $month_name parameter is expected to be the translated or
965          * translatable version of the month.
966          *
967          * @since 1.6
968          * @access public
969          *
970          * @param string $month_name Translated month to get abbreviated version
971          * @return string Translated abbreviated month
972          */
973         function get_month_abbrev( $month_name ) {
974                 return $this->month_abbrev[ $month_name ];
975         }
976
977         /**
978          * Retrieve translated version of meridiem string.
979          *
980          * The $meridiem parameter is expected to not be translated.
981          *
982          * @since 1.6
983          * @access public
984          *
985          * @param string $meridiem Either 'am', 'pm', 'AM', or 'PM'. Not translated version.
986          * @return string Translated version
987          */
988         function get_meridiem( $meridiem ) {
989                 return $this->meridiem[ $meridiem ];
990         }
991
992         /**
993          * Global variables are deprecated. For backwards compatibility only.
994          *
995          * @deprecated For backwards compatibility only.
996          * @access private
997          *
998          * @since 1.6
999          */
1000         function register_globals() {
1001                 $GLOBALS['weekday']         = $this->weekday;
1002                 $GLOBALS['weekday_initial'] = $this->weekday_initial;
1003                 $GLOBALS['weekday_abbrev']  = $this->weekday_abbrev;
1004                 $GLOBALS['month']           = $this->month;
1005                 $GLOBALS['month_abbrev']    = $this->month_abbrev;
1006         }
1007
1008         /**
1009          * Constructor which calls helper methods to set up object variables
1010          *
1011          * @uses YOURLS_Locale_Formats::init()
1012          * @uses YOURLS_Locale_Formats::register_globals()
1013          * @since 1.6
1014          *
1015          * @return YOURLS_Locale_Formats
1016          */
1017         function __construct() {
1018                 $this->init();
1019                 $this->register_globals();
1020         }
1021
1022         /**
1023          * Checks if current locale is RTL.
1024          *
1025          * @since 1.6
1026          * @return bool Whether locale is RTL.
1027          */
1028         function is_rtl() {
1029                 return 'rtl' == $this->text_direction;
1030         }
1031 }
1032
1033 /**
1034  * Loads a custom translation file (for a plugin, a theme, a public interface...) if locale is defined
1035  *
1036  * The .mo file should be named based on the domain with a dash, and then the locale exactly,
1037  * eg 'myplugin-pt_BR.mo'
1038  *
1039  * @since 1.6
1040  *
1041  * @param string $domain Unique identifier (the "domain") for retrieving translated strings
1042  * @param string $path Full path to directory containing MO files.
1043  * @return mixed Returns nothing if locale undefined, otherwise return bool: true on success, false on failure
1044  */
1045 function yourls_load_custom_textdomain( $domain, $path ) {
1046         $locale = yourls_apply_filter( 'load_custom_textdomain', yourls_get_locale(), $domain );
1047     if( !empty( $locale ) ) {
1048         $mofile = rtrim( $path, '/' ) . '/'. $domain . '-' . $locale . '.mo';
1049         return yourls_load_textdomain( $domain, $mofile );
1050     }
1051 }
1052
1053 /**
1054  * Checks if current locale is RTL. Stolen from WP.
1055  *
1056  * @since 1.6
1057  * @return bool Whether locale is RTL.
1058  */
1059 function yourls_is_rtl() {
1060         global $yourls_locale_formats;
1061         if( !isset( $yourls_locale_formats ) )
1062                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1063                 
1064         return $yourls_locale_formats->is_rtl();
1065 }
1066
1067 /**
1068  * Return translated weekday abbreviation (3 letters, eg 'Fri' for 'Friday')
1069  *
1070  * The $weekday var can be a textual string ('Friday'), a integer (0 to 6) or an empty string
1071  * If $weekday is an empty string, the function returns an array of all translated weekday abbrev
1072  *
1073  * @since 1.6
1074  * @param mixed $weekday A full textual weekday, eg "Friday", or an integer (0 = Sunday, 1 = Monday, .. 6 = Saturday)
1075  * @return mixed Translated weekday abbreviation, eg "Ven" (abbrev of "Vendredi") for "Friday" or 5, or array of all weekday abbrev
1076  */
1077 function yourls_l10n_weekday_abbrev( $weekday = '' ){
1078         global $yourls_locale_formats;
1079         if( !isset( $yourls_locale_formats ) )
1080                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1081                 
1082         if( $weekday === '' )
1083                 return $yourls_locale_formats->weekday_abbrev;
1084         
1085         if( is_int( $weekday ) ) {
1086                 $day = $yourls_locale_formats->weekday[ $weekday ];
1087                 return $yourls_locale_formats->weekday_abbrev[ $day ];
1088         } else {
1089                 return $yourls_locale_formats->weekday_abbrev[ yourls__( $weekday ) ];
1090         }
1091 }
1092
1093 /**
1094  * Return translated weekday initial (1 letter, eg 'F' for 'Friday')
1095  *
1096  * The $weekday var can be a textual string ('Friday'), a integer (0 to 6) or an empty string
1097  * If $weekday is an empty string, the function returns an array of all translated weekday initials
1098  *
1099  * @since 1.6
1100  * @param mixed $weekday A full textual weekday, eg "Friday", an integer (0 = Sunday, 1 = Monday, .. 6 = Saturday) or empty string
1101  * @return mixed Translated weekday initial, eg "V" (initial of "Vendredi") for "Friday" or 5, or array of all weekday initials
1102  */
1103 function yourls_l10n_weekday_initial( $weekday = '' ){
1104         global $yourls_locale_formats;
1105         if( !isset( $yourls_locale_formats ) )
1106                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1107                 
1108         if( $weekday === '' )
1109                 return $yourls_locale_formats->weekday_initial;
1110         
1111         if( is_int( $weekday ) ) {
1112                 $weekday = $yourls_locale_formats->weekday[ $weekday ];
1113                 return $yourls_locale_formats->weekday_initial[ $weekday ];
1114         } else {
1115                 return $yourls_locale_formats->weekday_initial[ yourls__( $weekday ) ];
1116         }
1117 }
1118
1119 /**
1120  * Return translated month abbrevation (3 letters, eg 'Nov' for 'November')
1121  *
1122  * The $month var can be a textual string ('November'), a integer (1 to 12), a two digits strings ('01' to '12), or an empty string
1123  * If $month is an empty string, the function returns an array of all translated abbrev months ('January' => 'Jan', ...)
1124  *
1125  * @since 1.6
1126  * @param mixed $month Empty string, a full textual weekday, eg "November", or an integer (1 = January, .., 12 = December)
1127  * @return mixed Translated month abbrev (eg "Nov"), or array of all translated abbrev months
1128  */
1129 function yourls_l10n_month_abbrev( $month = '' ){
1130         global $yourls_locale_formats;
1131         if( !isset( $yourls_locale_formats ) )
1132                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1133         
1134         if( $month === '' )
1135                 return $yourls_locale_formats->month_abbrev;
1136         
1137         if( intval( $month ) > 0 ) {
1138         $month = sprintf('%02d', intval( $month ) );
1139                 $month = $yourls_locale_formats->month[ $month ];
1140                 return $yourls_locale_formats->month_abbrev[ $month ];
1141         } else {
1142                 return $yourls_locale_formats->month_abbrev[ yourls__( $month ) ];
1143         }
1144 }
1145
1146 /**
1147  * Return array of all translated months
1148  *
1149  * @since 1.6
1150  * @return array Array of all translated months
1151  */
1152 function yourls_l10n_months(){
1153         global $yourls_locale_formats;
1154         if( !isset( $yourls_locale_formats ) )
1155                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1156         
1157         return $yourls_locale_formats->month;
1158 }