Issues (2756)

includes/functions-l10n.php (1 issue)

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