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