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