Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
MediaWikiPluralValidator.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\Validation\Validators;
5
6use Language;
10use MediaWiki\MediaWikiServices;
11use Parser;
12use ParserOptions;
13use PPFrame;
14use TMessage;
15
23 public function getIssues( TMessage $message, string $targetLanguage ): ValidationIssues {
24 $issues = new ValidationIssues();
25 $this->pluralCheck( $message, $issues );
26 $this->pluralFormsCheck( $message, $targetLanguage, $issues );
27
28 return $issues;
29 }
30
31 private function pluralCheck( TMessage $message, ValidationIssues $issues ): void {
32 $definition = $message->definition();
33 $translation = $message->translation();
34
35 if (
36 stripos( $definition, '{{plural:' ) !== false &&
37 stripos( $translation, '{{plural:' ) === false
38 ) {
39 $issue = new ValidationIssue( 'plural', 'missing', 'translate-checks-plural' );
40 $issues->add( $issue );
41 }
42 }
43
44 protected function pluralFormsCheck(
45 TMessage $message, string $code, ValidationIssues $issues
46 ): void {
47 $translation = $message->translation();
48 // Are there any plural forms for this language in this message?
49 if ( stripos( $translation, '{{plural:' ) === false ) {
50 return;
51 }
52
53 $plurals = self::getPluralForms( $translation );
54 $allowed = self::getPluralFormCount( $code );
55
56 foreach ( $plurals as $forms ) {
57 $forms = self::removeExplicitPluralForms( $forms );
58 $provided = count( $forms );
59
60 if ( $provided > $allowed ) {
61 $issue = new ValidationIssue(
62 'plural',
63 'forms',
64 'translate-checks-plural-forms',
65 [
66 [ 'COUNT', $provided ],
67 [ 'COUNT', $allowed ],
68 ]
69 );
70
71 $issues->add( $issue );
72 }
73
74 // Are the last two forms identical?
75 if ( $provided > 1 && $forms[$provided - 1] === $forms[$provided - 2] ) {
76 $issue = new ValidationIssue( 'plural', 'dupe', 'translate-checks-plural-dupe' );
77 $issues->add( $issue );
78 }
79 }
80 }
81
83 public static function getPluralFormCount( string $code ): int {
84 $forms = Language::factory( $code )->getPluralRules();
85
86 // +1 for the 'other' form
87 return count( $forms ) + 1;
88 }
89
96 public static function getPluralForms( string $translation ): array {
97 // Stores the forms from plural invocations
98 $plurals = [];
99
100 $cb = static function ( $parser, $frame, $args ) use ( &$plurals ) {
101 $forms = [];
102
103 foreach ( $args as $index => $form ) {
104 // The first arg is the number, we skip it
105 if ( $index !== 0 ) {
106 // Collect the raw text
107 $forms[] = $frame->expand( $form, PPFrame::RECOVER_ORIG );
108 // Expand the text to process embedded plurals
109 $frame->expand( $form );
110 }
111 }
112 $plurals[] = $forms;
113
114 return '';
115 };
116
117 // Setup parser
118 $services = MediaWikiServices::getInstance();
119 $parser = $services->getParserFactory()->create();
120 // Load the default magic words etc now.
121 $parser->firstCallInit();
122 // So that they don't overrider our own callback
123 $parser->setFunctionHook( 'plural', $cb, Parser::SFH_NO_HASH | Parser::SFH_OBJECT_ARGS );
124
125 // Setup things needed for preprocess
126 $title = null;
127 $options = ParserOptions::newFromUserAndLang(
128 $services->getUserFactory()->newAnonymous(),
129 $services->getLanguageFactory()->getLanguage( 'en' )
130 );
131
132 $parser->preprocess( $translation, $title, $options );
133
134 return $plurals;
135 }
136
138 public static function removeExplicitPluralForms( array $forms ): array {
139 // Handle explicit 0= and 1= forms
140 foreach ( $forms as $index => $form ) {
141 if ( preg_match( '/^[0-9]+=/', $form ) ) {
142 unset( $forms[$index] );
143 }
144 }
145
146 return array_values( $forms );
147 }
148}
add(ValidationIssue $issue)
Add a new validation issue to the collection.
Handles plural validation for MediaWiki inline plural syntax.
static getPluralFormCount(string $code)
Returns the number of plural forms MediaWiki supports for a language.
static removeExplicitPluralForms(array $forms)
Remove forms that start with an explicit number.
static getPluralForms(string $translation)
Ugly home made probably awfully slow looping parser that parses {{PLURAL}} instances from a message a...
Interface for message objects used by MessageCollection.
Definition Message.php:14
definition()
Get the message definition.
Definition Message.php:51
translation()
Get the message translation.