]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions-l10n.php
Make yourls_load_default_textdomain() return something.
[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_filters() 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                 if ( empty( $yourls_locale ) )
46                         $yourls_locale = 'en_US';
47         }
48         return yourls_apply_filters( 'get_locale', $yourls_locale );
49 }
50
51 /**
52  * Retrieves the translation of $text. If there is no translation, or
53  * the domain isn't loaded, the original text is returned.
54  *
55  * @see yourls__() Don't use yourls_translate() directly, use yourls__()
56  * @since 1.6
57  * @uses yourls_apply_filters() Calls 'translate' on domain translated text
58  *              with the untranslated text as second parameter.
59  *
60  * @param string $text Text to translate.
61  * @param string $domain Domain to retrieve the translated text.
62  * @return string Translated text
63  */
64 function yourls_translate( $text, $domain = 'default' ) {
65         $translations = yourls_get_translations_for_domain( $domain );
66         return yourls_apply_filters( 'translate', $translations->translate( $text ), $text, $domain );
67 }
68
69 /**
70  * Retrieves the translation of $text with a given $context. If there is no translation, or
71  * the domain isn't loaded, the original text is returned.
72  *
73  * Quite a few times, there will be collisions with similar translatable text
74  * found in more than two places but with different translated context.
75  *
76  * By including the context in the pot file translators can translate the two
77  * strings differently.
78  *
79  * @since 1.6
80  * @param string $text Text to translate.
81  * @param string $context Context.
82  * @param string $domain Domain to retrieve the translated text.
83  * @return string Translated text
84  */
85 function yourls_translate_with_context( $text, $context, $domain = 'default' ) {
86         $translations = yourls_get_translations_for_domain( $domain );
87         return yourls_apply_filters( 'translate_with_context', $translations->translate( $text, $context ), $text, $context, $domain );
88 }
89
90 /**
91  * Retrieves the translation of $text. If there is no translation, or
92  * the domain isn't loaded, the original text is returned.
93  *
94  * @see yourls_translate() An alias of yourls_translate()
95  * @since 1.6
96  *
97  * @param string $text Text to translate
98  * @param string $domain Optional. Domain to retrieve the translated text
99  * @return string Translated text
100  */
101 function yourls__( $text, $domain = 'default' ) {
102         return yourls_translate( $text, $domain );
103 }
104
105 /**
106  * Return a translated sprintf() string (mix yourls__() and sprintf() in one func)
107  *
108  * Instead of doing sprintf( yourls__( 'string %s' ), $arg ) you can simply use:
109  * yourls_s( 'string %s', $arg )
110  * This function accepts an arbitrary number of arguments:
111  * - first one will be the string to translate, eg "hello %s my name is %s"
112  * - following ones will be the sprintf arguments, eg "world" and "Ozh"
113  * - if there are more arguments passed than needed, the last one will be used as the translation domain
114  * This function will not accept a textdomain argument: do not use in plugins or outside YOURLS core.
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 ) ) {
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 without pipe
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.6
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 without pipe
273  */
274 function yourls_ex( $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_filters() 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_filters( '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_filters( '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_filters( '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_filters( 'load_textdomain_mofile', $mofile, $domain );
455
456         if ( !is_readable( $mofile ) ) return false;
457
458         $mo = new MO();
459         if ( !$mo->import_from_file( $mofile ) ) return false;
460
461         if ( isset( $yourls_l10n[$domain] ) )
462                 $mo->merge_with( $yourls_l10n[$domain] );
463
464         $yourls_l10n[$domain] = &$mo;
465
466         return true;
467 }
468
469 /**
470  * Unloads translations for a domain
471  *
472  * @since 1.6
473  * @param string $domain Textdomain to be unloaded
474  * @return bool Whether textdomain was unloaded
475  */
476 function yourls_unload_textdomain( $domain ) {
477         global $yourls_l10n;
478
479         $plugin_override = yourls_apply_filters( 'override_unload_textdomain', false, $domain );
480
481         if ( $plugin_override )
482                 return true;
483
484         yourls_do_action( 'unload_textdomain', $domain );
485
486         if ( isset( $yourls_l10n[$domain] ) ) {
487                 unset( $yourls_l10n[$domain] );
488                 return true;
489         }
490
491         return false;
492 }
493
494 /**
495  * Loads default translated strings based on locale.
496  *
497  * Loads the .mo file in YOURLS_LANG_DIR constant path from YOURLS root. The
498  * translated (.mo) file is named based on the locale.
499  *
500  * @since 1.6
501  * @return bool True on success, false on failure
502  */
503 function yourls_load_default_textdomain() {
504         $yourls_locale = yourls_get_locale();
505
506         return yourls_load_textdomain( 'default', YOURLS_LANG_DIR . "/$yourls_locale.mo" );
507
508 }
509
510 /**
511  * Returns the Translations instance for a domain. If there isn't one,
512  * returns empty Translations instance.
513  *
514  * @param string $domain
515  * @return object A Translation instance
516  */
517 function yourls_get_translations_for_domain( $domain ) {
518         global $yourls_l10n;
519         if ( !isset( $yourls_l10n[$domain] ) ) {
520                 $yourls_l10n[$domain] = new NOOP_Translations;
521         }
522         return $yourls_l10n[$domain];
523 }
524
525 /**
526  * Whether there are translations for the domain
527  *
528  * @since 1.6
529  * @param string $domain
530  * @return bool Whether there are translations
531  */
532 function yourls_is_textdomain_loaded( $domain ) {
533         global $yourls_l10n;
534         return isset( $yourls_l10n[$domain] );
535 }
536
537 /**
538  * Translates role name. Unused.
539  *
540  * Unused function for the moment, we'll see when there are roles.
541  * From the WP source: Since the role names are in the database and
542  * not in the source there are dummy gettext calls to get them into the POT
543  * file and this function properly translates them back.
544  *
545  * @since 1.6
546  */
547 function yourls_translate_user_role( $name ) {
548         return yourls_translate_with_context( $name, 'User role' );
549 }
550
551 /**
552  * Get all available languages (*.mo files) in a given directory. The default directory is YOURLS_LANG_DIR.
553  *
554  * @since 1.6
555  *
556  * @param string $dir A directory in which to search for language files. The default directory is YOURLS_LANG_DIR.
557  * @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.
558  */
559 function yourls_get_available_languages( $dir = null ) {
560         $languages = array();
561         
562         $dir = is_null( $dir) ? YOURLS_LANG_DIR : $dir;
563         
564         foreach( (array) glob( $dir . '/*.mo' ) as $lang_file ) {
565                 $languages[] = basename( $lang_file, '.mo' );
566         }
567         
568         return yourls_apply_filters( 'get_available_languages', $languages );
569 }
570
571 /**
572  * Return integer number to format based on the locale.
573  *
574  * @since 1.6
575  *
576  * @param int $number The number to convert based on locale.
577  * @param int $decimals Precision of the number of decimal places.
578  * @return string Converted number in string format.
579  */
580 function yourls_number_format_i18n( $number, $decimals = 0 ) {
581         global $yourls_locale_formats;
582         if( !isset( $yourls_locale_formats ) )
583                 $yourls_locale_formats = new YOURLS_Locale_Formats();
584                 
585         $formatted = number_format( $number, abs( intval( $decimals ) ), $yourls_locale_formats->number_format['decimal_point'], $yourls_locale_formats->number_format['thousands_sep'] );
586         return yourls_apply_filters( 'number_format_i18n', $formatted );
587 }
588
589 /**
590  * Return the date in localized format, based on timestamp.
591  *
592  * If the locale specifies the locale month and weekday, then the locale will
593  * take over the format for the date. If it isn't, then the date format string
594  * will be used instead.
595  *
596  * @since 1.6
597  *
598  * @param string   $dateformatstring Format to display the date.
599  * @param bool|int $unixtimestamp    Optional. Unix timestamp.
600  * @param bool     $gmt              Optional, default is false. Whether to convert to GMT for time.
601  * @return string The date, translated if locale specifies it.
602  */
603 function yourls_date_i18n( $dateformatstring, $unixtimestamp = false, $gmt = false ) {
604         global $yourls_locale_formats;
605         if( !isset( $yourls_locale_formats ) )
606                 $yourls_locale_formats = new YOURLS_Locale_Formats();
607
608         $i = $unixtimestamp;
609
610         if ( false === $i ) {
611                 if ( ! $gmt )
612                         $i = yourls_current_time( 'timestamp' );
613                 else
614                         $i = time();
615                 // we should not let date() interfere with our
616                 // specially computed timestamp
617                 $gmt = true;
618         }
619
620         // store original value for language with untypical grammars
621         // see http://core.trac.wordpress.org/ticket/9396
622         $req_format = $dateformatstring;
623
624         $datefunc = $gmt? 'gmdate' : 'date';
625
626         if ( ( !empty( $yourls_locale_formats->month ) ) && ( !empty( $yourls_locale_formats->weekday ) ) ) {
627                 $datemonth            = $yourls_locale_formats->get_month( $datefunc( 'm', $i ) );
628                 $datemonth_abbrev     = $yourls_locale_formats->get_month_abbrev( $datemonth );
629                 $dateweekday          = $yourls_locale_formats->get_weekday( $datefunc( 'w', $i ) );
630                 $dateweekday_abbrev   = $yourls_locale_formats->get_weekday_abbrev( $dateweekday );
631                 $datemeridiem         = $yourls_locale_formats->get_meridiem( $datefunc( 'a', $i ) );
632                 $datemeridiem_capital = $yourls_locale_formats->get_meridiem( $datefunc( 'A', $i ) );
633                 
634                 $dateformatstring = ' '.$dateformatstring;
635                 $dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . yourls_backslashit( $dateweekday_abbrev ), $dateformatstring );
636                 $dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . yourls_backslashit( $datemonth ), $dateformatstring );
637                 $dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . yourls_backslashit( $dateweekday ), $dateformatstring );
638                 $dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . yourls_backslashit( $datemonth_abbrev ), $dateformatstring );
639                 $dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . yourls_backslashit( $datemeridiem ), $dateformatstring );
640                 $dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . yourls_backslashit( $datemeridiem_capital ), $dateformatstring );
641
642                 $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
643         }
644         $timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
645         $timezone_formats_re = implode( '|', $timezone_formats );
646         if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) {
647         
648                 // TODO: implement a timezone option
649                 $timezone_string = yourls_get_option( 'timezone_string' );
650                 if ( $timezone_string ) {
651                         $timezone_object = timezone_open( $timezone_string );
652                         $date_object = date_create( null, $timezone_object );
653                         foreach( $timezone_formats as $timezone_format ) {
654                                 if ( false !== strpos( $dateformatstring, $timezone_format ) ) {
655                                         $formatted = date_format( $date_object, $timezone_format );
656                                         $dateformatstring = ' '.$dateformatstring;
657                                         $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . yourls_backslashit( $formatted ), $dateformatstring );
658                                         $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) -1 );
659                                 }
660                         }
661                 }
662         }
663         $j = @$datefunc( $dateformatstring, $i );
664         // allow plugins to redo this entirely for languages with untypical grammars
665         $j = yourls_apply_filters('date_i18n', $j, $req_format, $i, $gmt);
666         return $j;
667 }
668
669 /**
670  * Retrieve the current time based on specified type. Stolen from WP.
671  *
672  * The 'mysql' type will return the time in the format for MySQL DATETIME field.
673  * The 'timestamp' type will return the current timestamp.
674  *
675  * If $gmt is set to either '1' or 'true', then both types will use GMT time.
676  * if $gmt is false, the output is adjusted with the GMT offset in the WordPress option.
677  *
678  * @since 1.6
679  *
680  * @param string $type Either 'mysql' or 'timestamp'.
681  * @param int|bool $gmt Optional. Whether to use GMT timezone. Default is false.
682  * @return int|string String if $type is 'gmt', int if $type is 'timestamp'.
683  */
684 function yourls_current_time( $type, $gmt = 0 ) {
685         switch ( $type ) {
686                 case 'mysql':
687                         return ( $gmt ) ? gmdate( 'Y-m-d H:i:s' ) : gmdate( 'Y-m-d H:i:s', time() + YOURLS_HOURS_OFFSET * 3600 );
688                         break;
689                 case 'timestamp':
690                         return ( $gmt ) ? time() : time() + YOURLS_HOURS_OFFSET * 3600;
691                         break;
692         }
693 }
694
695
696 /**
697  * Class that loads the calendar locale.
698  *
699  * @since 1.6
700  */
701 class YOURLS_Locale_Formats {
702         /**
703          * Stores the translated strings for the full weekday names.
704          *
705          * @since 1.6
706          * @var array
707          * @access private
708          */
709         var $weekday;
710
711         /**
712          * Stores the translated strings for the one character weekday names.
713          *
714          * There is a hack to make sure that Tuesday and Thursday, as well
715          * as Sunday and Saturday, don't conflict. See init() method for more.
716          *
717          * @see YOURLS_Locale_Formats::init() for how to handle the hack.
718          *
719          * @since 1.6
720          * @var array
721          * @access private
722          */
723         var $weekday_initial;
724
725         /**
726          * Stores the translated strings for the abbreviated weekday names.
727          *
728          * @since 1.6
729          * @var array
730          * @access private
731          */
732         var $weekday_abbrev;
733
734         /**
735          * Stores the translated strings for the full month names.
736          *
737          * @since 1.6
738          * @var array
739          * @access private
740          */
741         var $month;
742
743         /**
744          * Stores the translated strings for the abbreviated month names.
745          *
746          * @since 1.6
747          * @var array
748          * @access private
749          */
750         var $month_abbrev;
751
752         /**
753          * Stores the translated strings for 'am' and 'pm'.
754          *
755          * Also the capitalized versions.
756          *
757          * @since 1.6
758          * @var array
759          * @access private
760          */
761         var $meridiem;
762
763         /**
764          * Stores the translated number format
765          *
766          * @since 1.6
767          * @var array
768          * @access private
769          */
770         var $number_format;
771
772         /**
773          * The text direction of the locale language.
774          *
775          * Default is left to right 'ltr'.
776          *
777          * @since 1.6
778          * @var string
779          * @access private
780          */
781         var $text_direction = 'ltr';
782
783         /**
784          * Sets up the translated strings and object properties.
785          *
786          * The method creates the translatable strings for various
787          * calendar elements. Which allows for specifying locale
788          * specific calendar names and text direction.
789          *
790          * @since 1.6
791          * @access private
792          */
793         function init() {
794                 // The Weekdays
795                 $this->weekday[0] = /* //translators: weekday */ yourls__( 'Sunday' );
796                 $this->weekday[1] = /* //translators: weekday */ yourls__( 'Monday' );
797                 $this->weekday[2] = /* //translators: weekday */ yourls__( 'Tuesday' );
798                 $this->weekday[3] = /* //translators: weekday */ yourls__( 'Wednesday' );
799                 $this->weekday[4] = /* //translators: weekday */ yourls__( 'Thursday' );
800                 $this->weekday[5] = /* //translators: weekday */ yourls__( 'Friday' );
801                 $this->weekday[6] = /* //translators: weekday */ yourls__( 'Saturday' );
802
803                 // The first letter of each day. The _%day%_initial suffix is a hack to make
804                 // sure the day initials are unique.
805                 $this->weekday_initial[yourls__( 'Sunday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'S_Sunday_initial' );
806                 $this->weekday_initial[yourls__( 'Monday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'M_Monday_initial' );
807                 $this->weekday_initial[yourls__( 'Tuesday' )]   = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'T_Tuesday_initial' );
808                 $this->weekday_initial[yourls__( 'Wednesday' )] = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'W_Wednesday_initial' );
809                 $this->weekday_initial[yourls__( 'Thursday' )]  = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'T_Thursday_initial' );
810                 $this->weekday_initial[yourls__( 'Friday' )]    = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'F_Friday_initial' );
811                 $this->weekday_initial[yourls__( 'Saturday' )]  = /* //translators: one-letter abbreviation of the weekday */ yourls__( 'S_Saturday_initial' );
812
813                 foreach ($this->weekday_initial as $weekday_ => $weekday_initial_) {
814                         $this->weekday_initial[$weekday_] = preg_replace('/_.+_initial$/', '', $weekday_initial_);
815                 }
816
817                 // Abbreviations for each day.
818                 $this->weekday_abbrev[ yourls__( 'Sunday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Sun' );
819                 $this->weekday_abbrev[ yourls__( 'Monday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Mon' );
820                 $this->weekday_abbrev[ yourls__( 'Tuesday' ) ]   = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Tue' );
821                 $this->weekday_abbrev[ yourls__( 'Wednesday' ) ] = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Wed' );
822                 $this->weekday_abbrev[ yourls__( 'Thursday' ) ]  = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Thu' );
823                 $this->weekday_abbrev[ yourls__( 'Friday' ) ]    = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Fri' );
824                 $this->weekday_abbrev[ yourls__( 'Saturday' ) ]  = /* //translators: three-letter abbreviation of the weekday */ yourls__( 'Sat' );
825
826                 // The Months
827                 $this->month['01'] = /* //translators: month name */ yourls__( 'January' );
828                 $this->month['02'] = /* //translators: month name */ yourls__( 'February' );
829                 $this->month['03'] = /* //translators: month name */ yourls__( 'March' );
830                 $this->month['04'] = /* //translators: month name */ yourls__( 'April' );
831                 $this->month['05'] = /* //translators: month name */ yourls__( 'May' );
832                 $this->month['06'] = /* //translators: month name */ yourls__( 'June' );
833                 $this->month['07'] = /* //translators: month name */ yourls__( 'July' );
834                 $this->month['08'] = /* //translators: month name */ yourls__( 'August' );
835                 $this->month['09'] = /* //translators: month name */ yourls__( 'September' );
836                 $this->month['10'] = /* //translators: month name */ yourls__( 'October' );
837                 $this->month['11'] = /* //translators: month name */ yourls__( 'November' );
838                 $this->month['12'] = /* //translators: month name */ yourls__( 'December' );
839
840                 // Abbreviations for each month. Uses the same hack as above to get around the
841                 // 'May' duplication.
842                 $this->month_abbrev[ yourls__( 'January' ) ]   = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jan_January_abbreviation' );
843                 $this->month_abbrev[ yourls__( 'February' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Feb_February_abbreviation' );
844                 $this->month_abbrev[ yourls__( 'March' ) ]     = /* //translators: three-letter abbreviation of the month */ yourls__( 'Mar_March_abbreviation' );
845                 $this->month_abbrev[ yourls__( 'April' ) ]     = /* //translators: three-letter abbreviation of the month */ yourls__( 'Apr_April_abbreviation' );
846                 $this->month_abbrev[ yourls__( 'May' ) ]       = /* //translators: three-letter abbreviation of the month */ yourls__( 'May_May_abbreviation' );
847                 $this->month_abbrev[ yourls__( 'June' ) ]      = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jun_June_abbreviation' );
848                 $this->month_abbrev[ yourls__( 'July' ) ]      = /* //translators: three-letter abbreviation of the month */ yourls__( 'Jul_July_abbreviation' );
849                 $this->month_abbrev[ yourls__( 'August' ) ]    = /* //translators: three-letter abbreviation of the month */ yourls__( 'Aug_August_abbreviation' );
850                 $this->month_abbrev[ yourls__( 'September' ) ] = /* //translators: three-letter abbreviation of the month */ yourls__( 'Sep_September_abbreviation' );
851                 $this->month_abbrev[ yourls__( 'October' ) ]   = /* //translators: three-letter abbreviation of the month */ yourls__( 'Oct_October_abbreviation' );
852                 $this->month_abbrev[ yourls__( 'November' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Nov_November_abbreviation' );
853                 $this->month_abbrev[ yourls__( 'December' ) ]  = /* //translators: three-letter abbreviation of the month */ yourls__( 'Dec_December_abbreviation' );
854
855                 foreach ($this->month_abbrev as $month_ => $month_abbrev_) {
856                         $this->month_abbrev[$month_] = preg_replace('/_.+_abbreviation$/', '', $month_abbrev_);
857                 }
858
859                 // The Meridiems
860                 $this->meridiem['am'] = yourls__( 'am' );
861                 $this->meridiem['pm'] = yourls__( 'pm' );
862                 $this->meridiem['AM'] = yourls__( 'AM' );
863                 $this->meridiem['PM'] = yourls__( 'PM' );
864
865                 // Numbers formatting
866                 // See http://php.net/number_format
867
868                 /* //translators: $thousands_sep argument for http://php.net/number_format, default is , */
869                 $trans = yourls__( 'number_format_thousands_sep' );
870                 $this->number_format['thousands_sep'] = ('number_format_thousands_sep' == $trans) ? ',' : $trans;
871
872                 /* //translators: $dec_point argument for http://php.net/number_format, default is . */
873                 $trans = yourls__( 'number_format_decimal_point' );
874                 $this->number_format['decimal_point'] = ('number_format_decimal_point' == $trans) ? '.' : $trans;
875
876                 // Set text direction.
877                 if ( isset( $GLOBALS['text_direction'] ) )
878                         $this->text_direction = $GLOBALS['text_direction'];
879                 /* //translators: 'rtl' or 'ltr'. This sets the text direction for YOURLS. */
880                 elseif ( 'rtl' == yourls_x( 'ltr', 'text direction' ) )
881                         $this->text_direction = 'rtl';
882         }
883
884         /**
885          * Retrieve the full translated weekday word.
886          *
887          * Week starts on translated Sunday and can be fetched
888          * by using 0 (zero). So the week starts with 0 (zero)
889          * and ends on Saturday with is fetched by using 6 (six).
890          *
891          * @since 1.6
892          * @access public
893          *
894          * @param int $weekday_number 0 for Sunday through 6 Saturday
895          * @return string Full translated weekday
896          */
897         function get_weekday( $weekday_number ) {
898                 return $this->weekday[ $weekday_number ];
899         }
900
901         /**
902          * Retrieve the translated weekday initial.
903          *
904          * The weekday initial is retrieved by the translated
905          * full weekday word. When translating the weekday initial
906          * pay attention to make sure that the starting letter does
907          * not conflict.
908          *
909          * @since 1.6
910          * @access public
911          *
912          * @param string $weekday_name
913          * @return string
914          */
915         function get_weekday_initial( $weekday_name ) {
916                 return $this->weekday_initial[ $weekday_name ];
917         }
918
919         /**
920          * Retrieve the translated weekday abbreviation.
921          *
922          * The weekday abbreviation is retrieved by the translated
923          * full weekday word.
924          *
925          * @since 1.6
926          * @access public
927          *
928          * @param string $weekday_name Full translated weekday word
929          * @return string Translated weekday abbreviation
930          */
931         function get_weekday_abbrev( $weekday_name ) {
932                 return $this->weekday_abbrev[ $weekday_name ];
933         }
934
935         /**
936          * Retrieve the full translated month by month number.
937          *
938          * The $month_number parameter has to be a string
939          * because it must have the '0' in front of any number
940          * that is less than 10. Starts from '01' and ends at
941          * '12'.
942          *
943          * You can use an integer instead and it will add the
944          * '0' before the numbers less than 10 for you.
945          *
946          * @since 1.6
947          * @access public
948          *
949          * @param string|int $month_number '01' through '12'
950          * @return string Translated full month name
951          */
952         function get_month( $month_number ) {
953                 return $this->month[ sprintf( '%02s', $month_number ) ];                
954         }
955
956         /**
957          * Retrieve translated version of month abbreviation string.
958          *
959          * The $month_name parameter is expected to be the translated or
960          * translatable version of the month.
961          *
962          * @since 1.6
963          * @access public
964          *
965          * @param string $month_name Translated month to get abbreviated version
966          * @return string Translated abbreviated month
967          */
968         function get_month_abbrev( $month_name ) {
969                 return $this->month_abbrev[ $month_name ];
970         }
971
972         /**
973          * Retrieve translated version of meridiem string.
974          *
975          * The $meridiem parameter is expected to not be translated.
976          *
977          * @since 1.6
978          * @access public
979          *
980          * @param string $meridiem Either 'am', 'pm', 'AM', or 'PM'. Not translated version.
981          * @return string Translated version
982          */
983         function get_meridiem( $meridiem ) {
984                 return $this->meridiem[ $meridiem ];
985         }
986
987         /**
988          * Global variables are deprecated. For backwards compatibility only.
989          *
990          * @deprecated For backwards compatibility only.
991          * @access private
992          *
993          * @since 1.6
994          */
995         function register_globals() {
996                 $GLOBALS['weekday']         = $this->weekday;
997                 $GLOBALS['weekday_initial'] = $this->weekday_initial;
998                 $GLOBALS['weekday_abbrev']  = $this->weekday_abbrev;
999                 $GLOBALS['month']           = $this->month;
1000                 $GLOBALS['month_abbrev']    = $this->month_abbrev;
1001         }
1002
1003         /**
1004          * Constructor which calls helper methods to set up object variables
1005          *
1006          * @uses YOURLS_Locale_Formats::init()
1007          * @uses YOURLS_Locale_Formats::register_globals()
1008          * @since 1.6
1009          *
1010          * @return YOURLS_Locale_Formats
1011          */
1012         function __construct() {
1013                 $this->init();
1014                 $this->register_globals();
1015         }
1016
1017         /**
1018          * Checks if current locale is RTL.
1019          *
1020          * @since 1.6
1021          * @return bool Whether locale is RTL.
1022          */
1023         function is_rtl() {
1024                 return 'rtl' == $this->text_direction;
1025         }
1026 }
1027
1028 /**
1029  * Loads a custom translation file (for a plugin, a theme, a public interface...)
1030  *
1031  * The .mo file should be named based on the domain with a dash, and then the locale exactly,
1032  * eg 'myplugin-pt_BR.mo'
1033  *
1034  * @since 1.6
1035  *
1036  * @param string $domain Unique identifier (the "domain") for retrieving translated strings
1037  * @param string $path Full path to directory containing MO files.
1038  * @return bool True on success, false on failure
1039  */
1040 function yourls_load_custom_textdomain( $domain, $path ) {
1041         $locale = yourls_apply_filters( 'load_custom_textdomain', yourls_get_locale(), $domain );
1042         $mofile = trim( $path, '/' ) . '/'. $domain . '-' . $locale . '.mo';
1043
1044         return yourls_load_textdomain( $domain, $mofile );
1045 }
1046
1047 /**
1048  * Checks if current locale is RTL. Stolen from WP.
1049  *
1050  * @since 1.6
1051  * @return bool Whether locale is RTL.
1052  */
1053 function yourls_is_rtl() {
1054         global $yourls_locale_formats;
1055         if( !isset( $yourls_locale_formats ) )
1056                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1057                 
1058         return $yourls_locale_formats->is_rtl();
1059 }
1060
1061 /**
1062  * Return translated weekday abbreviation (3 letters, eg 'Fri' for 'Friday')
1063  *
1064  * The $weekday var can be a textual string ('Friday'), a integer (0 to 6) or an empty string
1065  * If $weekday is an empty string, the function returns an array of all translated weekday abbrev
1066  *
1067  * @since 1.6
1068  * @param mixed $weekday A full textual weekday, eg "Friday", or an integer (0 = Sunday, 1 = Monday, .. 6 = Saturday)
1069  * @return mixed Translated weekday abbreviation, eg "Ven" (abbrev of "Vendredi") for "Friday" or 5, or array of all weekday abbrev
1070  */
1071 function yourls_l10n_weekday_abbrev( $weekday = '' ){
1072         global $yourls_locale_formats;
1073         if( !isset( $yourls_locale_formats ) )
1074                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1075                 
1076         if( $weekday === '' )
1077                 return $yourls_locale_formats->weekday_abbrev;
1078         
1079         if( is_int( $weekday ) ) {
1080                 $day = $yourls_locale_formats->weekday[ $weekday ];
1081                 return $yourls_locale_formats->weekday_abbrev[ $day ];
1082         } else {
1083                 return $yourls_locale_formats->weekday_abbrev[ yourls__( $weekday ) ];
1084         }
1085 }
1086
1087 /**
1088  * Return translated weekday initial (1 letter, eg 'F' for 'Friday')
1089  *
1090  * The $weekday var can be a textual string ('Friday'), a integer (0 to 6) or an empty string
1091  * If $weekday is an empty string, the function returns an array of all translated weekday initials
1092  *
1093  * @since 1.6
1094  * @param mixed $weekday A full textual weekday, eg "Friday", an integer (0 = Sunday, 1 = Monday, .. 6 = Saturday) or empty string
1095  * @return mixed Translated weekday initial, eg "V" (initial of "Vendredi") for "Friday" or 5, or array of all weekday initials
1096  */
1097 function yourls_l10n_weekday_initial( $weekday = '' ){
1098         global $yourls_locale_formats;
1099         if( !isset( $yourls_locale_formats ) )
1100                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1101                 
1102         if( $weekday === '' )
1103                 return $yourls_locale_formats->weekday_initial;
1104         
1105         if( is_int( $weekday ) ) {
1106                 $weekday = $yourls_locale_formats->weekday[ $weekday ];
1107                 return $yourls_locale_formats->weekday_initial[ $weekday ];
1108         } else {
1109                 return $yourls_locale_formats->weekday_initial[ yourls__( $weekday ) ];
1110         }
1111 }
1112
1113 /**
1114  * Return translated month abbrevation (3 letters, eg 'Nov' for 'November')
1115  *
1116  * 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
1117  * If $month is an empty string, the function returns an array of all translated abbrev months ('January' => 'Jan', ...)
1118  *
1119  * @since 1.6
1120  * @param mixed $month Empty string, a full textual weekday, eg "November", or an integer (1 = January, .., 12 = December)
1121  * @return mixed Translated month abbrev (eg "Nov"), or array of all translated abbrev months
1122  */
1123 function yourls_l10n_month_abbrev( $month = '' ){
1124         global $yourls_locale_formats;
1125         if( !isset( $yourls_locale_formats ) )
1126                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1127         
1128         if( $month === '' )
1129                 return $yourls_locale_formats->month_abbrev;
1130         
1131         if( intval( $month ) > 0 ) {
1132                 $month = $yourls_locale_formats->month[ $month ];
1133                 return $yourls_locale_formats->month_abbrev[ $month ];
1134         } else {
1135                 return $yourls_locale_formats->month_abbrev[ yourls__( $month ) ];
1136         }
1137 }
1138
1139 /**
1140  * Return array of all translated months
1141  *
1142  * @since 1.6
1143  * @return array Array of all translated months
1144  */
1145 function yourls_l10n_months(){
1146         global $yourls_locale_formats;
1147         if( !isset( $yourls_locale_formats ) )
1148                 $yourls_locale_formats = new YOURLS_Locale_Formats();
1149         
1150         return $yourls_locale_formats->month;
1151 }