Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
72.73% |
16 / 22 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
JsonLocalizer | |
72.73% |
16 / 22 |
|
80.00% |
4 / 5 |
13.45 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
localizeJson | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
localizeJsonPairs | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
localizeValue | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
getFormattedMessage | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Rest; |
4 | |
5 | use Wikimedia\Message\MessageValue; |
6 | |
7 | /** |
8 | * Utility class for json localization needs in the REST API. |
9 | * |
10 | * Much of this involves replacing custom name/value pairs (herein referred to as i18n pairs) with |
11 | * translated standard name/value pairs (herein referred to as schema pairs). For example, OpenAPI |
12 | * specifications include a "description" field represented as a name/value pair, like: |
13 | * "description": "My description". |
14 | * We allow a "x-i18n-description" field, whose value is a MediaWiki message key, like: |
15 | * "x-i18n-description": "rest-my-description-message-key" |
16 | * Functions in this class will replace the "x-i18n-description" with a translated "description". |
17 | */ |
18 | class JsonLocalizer { |
19 | private ResponseFactory $responseFactory; |
20 | |
21 | private const LOCALIZATION_PREFIX = 'x-i18n-'; |
22 | |
23 | /** |
24 | * @param ResponseFactory $responseFactory |
25 | * |
26 | * @internal |
27 | */ |
28 | public function __construct( |
29 | ResponseFactory $responseFactory |
30 | ) { |
31 | $this->responseFactory = $responseFactory; |
32 | } |
33 | |
34 | /** |
35 | * Recursively localizes name/value pairs, where the name begins with "x-i18-n-" |
36 | * |
37 | * @param array $json the input json, as a PHP array |
38 | * |
39 | * @return array the adjusted json, or the unchanged json if no adjustments were made |
40 | */ |
41 | public function localizeJson( array $json ): array { |
42 | return $this->localizeJsonPairs( $json, self::LOCALIZATION_PREFIX ); |
43 | } |
44 | |
45 | /** |
46 | * Recursively localizes name/value pairs. Pairs to be localized are identified by prefix, |
47 | * and values must be message keys. |
48 | * |
49 | * The resulting key has the prefix removed, and a localized value. For example this pair: |
50 | * 'x-i18n-description' => 'mw-rest-my-description-message-key' |
51 | * Would be localized to something like: |
52 | * 'description' => 'My Description' |
53 | * |
54 | * @param array $json the input json, as a PHP array |
55 | * @param string $i18nPrefix keys beginning with this prefix will be localized |
56 | * |
57 | * @return array the adjusted json, or the unchanged json if no adjustments were made |
58 | */ |
59 | private function localizeJsonPairs( array $json, string $i18nPrefix ): array { |
60 | // Use a reference for $value, because it is potentially modified within the loop. |
61 | foreach ( $json as $key => &$value ) { |
62 | if ( is_array( $value ) ) { |
63 | $value = $this->localizeJsonPairs( $value, $i18nPrefix ); |
64 | } elseif ( str_starts_with( $key, $i18nPrefix ) ) { |
65 | $newKey = substr( $key, strlen( $i18nPrefix ) ); |
66 | |
67 | // Add the description to the top of the json, for visibility in raw specs |
68 | $msg = new MessageValue( $value ); |
69 | $pair = [ $newKey => $msg ]; |
70 | $json = $pair + $json; |
71 | $json[$newKey] = $this->getFormattedMessage( $msg ); |
72 | |
73 | unset( $json[$key] ); |
74 | } |
75 | } |
76 | return $json; |
77 | } |
78 | |
79 | /** |
80 | * Returns the localized value if possible, or the non-localized value if one is |
81 | * available, or null otherwise. Translates only top-level keys. Useful when the value |
82 | * corresponding to the input key may be either a string to be used as-is or a MessageValue |
83 | * object to be localized. |
84 | * |
85 | * @param array $json the input json, as a PHP array |
86 | * @param string $key key name of the field |
87 | * |
88 | * @return ?string |
89 | */ |
90 | public function localizeValue( array $json, string $key ): ?string { |
91 | $value = null; |
92 | |
93 | if ( array_key_exists( $key, $json ) ) { |
94 | if ( $json[ $key ] instanceof MessageValue ) { |
95 | $value = $this->getFormattedMessage( $json[ $key ] ); |
96 | } else { |
97 | $value = $json[ $key ]; |
98 | } |
99 | } |
100 | |
101 | return $value; |
102 | } |
103 | |
104 | /** |
105 | * Tries to return one formatted string for a message key or message value object. |
106 | * |
107 | * @param string|MessageValue $message the message format, or a key representing it |
108 | * |
109 | * @return string |
110 | */ |
111 | public function getFormattedMessage( $message ): string { |
112 | if ( !$message instanceof MessageValue ) { |
113 | $message = new MessageValue( $message ); |
114 | } |
115 | |
116 | // TODO: consider if we want to request a specific preferred language |
117 | return $this->responseFactory->getFormattedMessage( $message ); |
118 | } |
119 | |
120 | } |