summaryrefslogtreecommitdiff
blob: 36c6affdbdf501097c9c338072c8c393e5395379 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php
declare( strict_types = 1 );

namespace MediaWiki\Extension\Translate\Validation\Validators;

use MediaWiki\Extension\Translate\Utilities\GettextPlural;
use MediaWiki\Extension\Translate\Validation\MessageValidator;
use MediaWiki\Extension\Translate\Validation\ValidationIssue;
use MediaWiki\Extension\Translate\Validation\ValidationIssues;
use TMessage;

/**
 * @license GPL-2.0-or-later
 * @since 2019.09
 */
class GettextPluralValidator implements MessageValidator {
	public function getIssues( TMessage $message, string $targetLanguage ): ValidationIssues {
		$issues = new ValidationIssues();

		$pluralRule = GettextPlural::getPluralRule( $targetLanguage );
		// Skip validation for languages for which we do not know the plural rule
		if ( !$pluralRule ) {
			return $issues;
		}

		$definition = $message->definition();
		$translation = $message->translation();
		$expectedPluralCount = GettextPlural::getPluralCount( $pluralRule );
		$definitionHasPlural = GettextPlural::hasPlural( $definition );
		$translationHasPlural = GettextPlural::hasPlural( $translation );

		$presence = $this->pluralPresenceCheck(
			$definitionHasPlural,
			$translationHasPlural,
			$expectedPluralCount
		);

		if ( $presence === 'ok' ) {
			[ $msgcode, $data ] = $this->pluralFormCountCheck( $translation, $expectedPluralCount );
			if ( $msgcode === 'invalid-count' ) {
				$issue = new ValidationIssue(
					'plural',
					'forms',
					'translate-checks-gettext-plural-count',
					[
						[ 'COUNT', $expectedPluralCount ],
						[ 'COUNT', $data[ 'count' ] ],
					]
				);
				$issues->add( $issue );
			}
		} elseif ( $presence === 'missing' ) {
			$issue = new ValidationIssue(
				'plural',
				'missing',
				'translate-checks-gettext-plural-missing'
			);
			$issues->add( $issue );
		} elseif ( $presence === 'unsupported' ) {
			$issue = new ValidationIssue(
				'plural',
				'unsupported',
				'translate-checks-gettext-plural-unsupported'
			);
			$issues->add( $issue );
		}
		// else not-applicable: Plural is not present in translation, but that is fine

		return $issues;
	}

	private function pluralPresenceCheck(
		$definitionHasPlural,
		$translationHasPlural,
		$expectedPluralCount
	) {
		if ( !$definitionHasPlural && $translationHasPlural ) {
			return 'unsupported';
		} elseif ( $definitionHasPlural && !$translationHasPlural ) {
			if ( $expectedPluralCount > 1 ) {
				return 'missing';
			} else {
				// It's okay to omit plural completely for languages without variance
				return 'not-applicable';
			}
		} elseif ( !$definitionHasPlural && !$translationHasPlural ) {
			return 'not-applicable';
		}

		// Both have plural
		return 'ok';
	}

	private function pluralFormCountCheck( $text, $expectedPluralCount ) {
		[ , $instanceMap ] = GettextPlural::parsePluralForms( $text );

		foreach ( $instanceMap as $forms ) {
			$formsCount = count( $forms );
			if ( $formsCount !== $expectedPluralCount ) {
				return [ 'invalid-count', [ 'count' => $formsCount ] ];
			}
		}

		return [ 'ok', [] ];
	}
}

class_alias( GettextPluralValidator::class, '\MediaWiki\Extensions\Translate\GettextPluralValidator' );