Issues (2756)

includes/functions-l10n.php (17 issues)

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 ) {
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
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
660
	 * @var array
661
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
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
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
674
	 * @var array
675
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
676
	 */
677
	var $weekday_initial;
678
679
	/**
680
	 * Stores the translated strings for the abbreviated weekday names.
681
	 *
682
	 * @since 1.6
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
683
	 * @var array
684
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
685
	 */
686
	var $weekday_abbrev;
687
688
	/**
689
	 * Stores the translated strings for the full month names.
690
	 *
691
	 * @since 1.6
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
692
	 * @var array
693
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
694
	 */
695
	var $month;
696
697
	/**
698
	 * Stores the translated strings for the abbreviated month names.
699
	 *
700
	 * @since 1.6
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
701
	 * @var array
702
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
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
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
712
	 * @var array
713
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
714
	 */
715
	var $meridiem;
716
717
	/**
718
	 * Stores the translated number format
719
	 *
720
	 * @since 1.6
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
721
	 * @var array
722
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
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
0 ignored issues
show
Coding Style Documentation introduced by
@since tag is not allowed in member variable comment
Loading history...
732
	 * @var string
733
	 * @access private
0 ignored issues
show
Coding Style Documentation introduced by
@access tag is not allowed in member variable comment
Loading history...
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();
0 ignored issues
show
Deprecated Code introduced by
The function YOURLS_Locale_Formats::register_globals() has been deprecated: For backwards compatibility only. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

968
		/** @scrutinizer ignore-deprecated */ $this->register_globals();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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