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