Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 81
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
YamlFormat
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 8
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getFileExtensions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 readFromVariable
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 writeReal
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
42
 doHeader
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 doAuthors
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 isContentEqual
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExtraSchema
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\FileFormatSupport;
5
6use FileBasedMessageGroup;
7use MediaWiki\Extension\Translate\MessageGroupConfiguration\MetaYamlSchemaExtender;
8use MediaWiki\Extension\Translate\MessageLoading\Message;
9use MediaWiki\Extension\Translate\MessageLoading\MessageCollection;
10use MediaWiki\Extension\Translate\MessageProcessing\ArrayFlattener;
11use MediaWiki\Extension\Translate\Utilities\Utilities;
12use MediaWiki\Extension\Translate\Utilities\Yaml;
13use RuntimeException;
14
15/**
16 * Implements support for message storage in YAML format.
17 *
18 * This class adds new key into FILES section: \c codeAsRoot.
19 * If it is set to true, all messages will under language code.
20 * @ingroup FileFormatSupport
21 */
22class YamlFormat extends SimpleFormat implements MetaYamlSchemaExtender {
23    private ArrayFlattener $flattener;
24
25    public function __construct( FileBasedMessageGroup $group ) {
26        parent::__construct( $group );
27
28        // Obtains object used to flatten and unflatten arrays. In this implementation
29        // we use the ArrayFlattener class which also supports CLDR pluralization rules.
30        $this->flattener = new ArrayFlattener(
31            $this->extra['nestingSeparator'] ?? '.',
32            $this->extra['parseCLDRPlurals'] ?? false
33        );
34    }
35
36    public function getFileExtensions(): array {
37        return [ '.yaml', '.yml' ];
38    }
39
40    /** @inheritDoc */
41    public function readFromVariable( string $data ): array {
42        // Authors first.
43        $matches = [];
44        preg_match_all( '/^#\s*Author:\s*(.*)$/m', $data, $matches );
45        $authors = $matches[1];
46
47        // Then messages.
48        $messages = Yaml::loadString( $data );
49
50        // Some groups have messages under language code
51        if ( isset( $this->extra['codeAsRoot'] ) ) {
52            $messages = array_shift( $messages ) ?? [];
53        }
54
55        $messages = $this->flattener->flatten( $messages );
56        $messages = $this->group->getMangler()->mangleArray( $messages );
57        foreach ( $messages as &$value ) {
58            $value = rtrim( $value, "\n" );
59        }
60
61        return [
62            'AUTHORS' => $authors,
63            'MESSAGES' => $messages,
64        ];
65    }
66
67    protected function writeReal( MessageCollection $collection ): string {
68        $output = $this->doHeader( $collection );
69        $output .= $this->doAuthors( $collection );
70
71        $mangler = $this->group->getMangler();
72
73        $messages = [];
74
75        $collection->filter( MessageCollection::FILTER_HAS_TRANSLATION, MessageCollection::INCLUDE_MATCHING );
76        /** @var Message $m */
77        foreach ( $collection as $key => $m ) {
78            $key = $mangler->unmangle( $key );
79            $value = $m->translation();
80            if ( $value === null ) {
81                throw new RuntimeException( "Expected translation to be present for $key, but found null." );
82            }
83
84            $value = str_replace( TRANSLATE_FUZZY, '', $value );
85            if ( $value === '' ) {
86                continue;
87            }
88
89            $messages[$key] = $value;
90        }
91
92        if ( !count( $messages ) ) {
93            return '';
94        }
95        $messages = $this->flattener->unflatten( $messages );
96
97        // Some groups have messages under language code.
98        if ( isset( $this->extra['codeAsRoot'] ) ) {
99            $code = $this->group->mapCode( $collection->code );
100            $messages = [ $code => $messages ];
101        }
102
103        $output .= Yaml::dump( $messages );
104
105        return $output;
106    }
107
108    private function doHeader( MessageCollection $collection ): string {
109        global $wgSitename;
110        global $wgTranslateYamlLibrary;
111
112        $code = $collection->code;
113        $name = Utilities::getLanguageName( $code );
114        $native = Utilities::getLanguageName( $code, $code );
115        $output = "# Messages for $name ($native)\n";
116        $output .= "# Exported from $wgSitename\n";
117
118        if ( isset( $wgTranslateYamlLibrary ) ) {
119            $output .= "# Export driver: $wgTranslateYamlLibrary\n";
120        }
121
122        return $output;
123    }
124
125    private function doAuthors( MessageCollection $collection ): string {
126        $output = '';
127        $authors = $collection->getAuthors();
128        $authors = $this->filterAuthors( $authors, $collection->code );
129
130        foreach ( $authors as $author ) {
131            $output .= "# Author: $author\n";
132        }
133
134        return $output;
135    }
136
137    public function isContentEqual( ?string $a, ?string $b ): bool {
138        return $this->flattener->compareContent( $a, $b );
139    }
140
141    public static function getExtraSchema(): array {
142        return [
143            'root' => [
144                '_type' => 'array',
145                '_children' => [
146                    'FILES' => [
147                        '_type' => 'array',
148                        '_children' => [
149                            'codeAsRoot' => [
150                                '_type' => 'boolean',
151                            ],
152                            'nestingSeparator' => [
153                                '_type' => 'text',
154                            ],
155                            'parseCLDRPlurals' => [
156                                '_type' => 'boolean',
157                            ]
158                        ]
159                    ]
160                ]
161            ]
162        ];
163    }
164}
165
166class_alias( YamlFormat::class, 'YamlFFS' );