Skip to content

Commit 4847549

Browse files
authored
feat: add reader/writer format for Crowdin (#26)
1 parent beec2cf commit 4847549

18 files changed

Lines changed: 552 additions & 73 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
### Added
1111

12+
- Add [Crowdin](https://crowdin.com) as a format for writing and reading extracted messages
1213
- Add `pseudo-locale` console command to allow conversion of a locale to one of the supported pseudo-locales (`en-XA`, `en-XB`, `xx-AC`, `xx-HA`, and `xx-LS`).
1314
- Provide `--flatten` extraction option to tell the extractor to hoist selectors and flatten sentences as much as possible. For example, `I have {count, plural, one{a dog} other{many dogs}}` becomes `{count, plural, one{I have a dog} other{I have many dogs}}`. The goal is to provide as many full sentences as possible, since fragmented sentences are not translator-friendly.
1415
- Provide `--add-missing-ids` extraction option to update source code with auto-generated identifiers

src/Format/Format.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
*/
2828
class Format
2929
{
30+
public const CHROME = 'chrome';
31+
public const CROWDIN = 'crowdin';
3032
public const FORMATPHP = 'formatphp';
3133
public const SIMPLE = 'simple';
3234
public const SMARTLING = 'smartling';

src/Format/Reader/ChromeReader.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
/**
4+
* This file is part of skillshare/formatphp
5+
*
6+
* skillshare/formatphp is open source software: you can distribute
7+
* it and/or modify it under the terms of the MIT License
8+
* (the "License"). You may not use this file except in
9+
* compliance with the License.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14+
* implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*
17+
* @copyright Copyright (c) Skillshare, Inc. <https://www.skillshare.com>
18+
* @license https://opensource.org/licenses/MIT MIT License
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace FormatPHP\Format\Reader;
24+
25+
use FormatPHP\Exception\InvalidMessageShapeException;
26+
use FormatPHP\Format\ReaderInterface;
27+
use FormatPHP\Message;
28+
use FormatPHP\MessageCollection;
29+
30+
use function gettype;
31+
use function is_array;
32+
use function is_string;
33+
use function sprintf;
34+
35+
/**
36+
* Returns a MessageCollection parsed from JSON-decoded data that was written
37+
* using {@see ChromeWriter}
38+
*/
39+
class ChromeReader implements ReaderInterface
40+
{
41+
/**
42+
* @inheritdoc
43+
*/
44+
public function __invoke(array $data): MessageCollection
45+
{
46+
$messages = new MessageCollection();
47+
48+
/**
49+
* @var string $messageId
50+
* @var array{message: string} $message
51+
*/
52+
foreach ($data as $messageId => $message) {
53+
$this->validateShape($messageId, $message);
54+
55+
$messages[] = new Message($messageId, $message['message']);
56+
}
57+
58+
return $messages;
59+
}
60+
61+
/**
62+
* @param array-key $messageId
63+
* @param mixed $message
64+
*
65+
* @throws InvalidMessageShapeException
66+
*/
67+
private function validateShape($messageId, $message): void
68+
{
69+
if (!is_string($messageId)) {
70+
throw new InvalidMessageShapeException(sprintf(
71+
'%s expects a string message ID; received %s',
72+
static::class,
73+
gettype($messageId),
74+
));
75+
}
76+
77+
if (!is_array($message) || !is_string($message['message'] ?? null)) {
78+
throw new InvalidMessageShapeException(sprintf(
79+
'%s expects a string message property; message does not exist or is not a string',
80+
static::class,
81+
));
82+
}
83+
}
84+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/**
4+
* This file is part of skillshare/formatphp
5+
*
6+
* skillshare/formatphp is open source software: you can distribute
7+
* it and/or modify it under the terms of the MIT License
8+
* (the "License"). You may not use this file except in
9+
* compliance with the License.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14+
* implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*
17+
* @copyright Copyright (c) Skillshare, Inc. <https://www.skillshare.com>
18+
* @license https://opensource.org/licenses/MIT MIT License
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace FormatPHP\Format\Reader;
24+
25+
/**
26+
* Returns a MessageCollection parsed from JSON-decoded data that was written
27+
* using {@see CrowdinWriter}
28+
*/
29+
class CrowdinReader extends ChromeReader
30+
{
31+
}

src/Format/Reader/FormatPHPReader.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
use FormatPHP\Exception\InvalidMessageShapeException;
2626
use FormatPHP\Format\ReaderInterface;
27-
use FormatPHP\Format\Writer\FormatPHPWriter;
2827
use FormatPHP\Message;
2928
use FormatPHP\MessageCollection;
3029

@@ -35,9 +34,7 @@
3534

3635
/**
3736
* Returns a MessageCollection parsed from JSON-decoded data that was written
38-
* using Writer\Format\FormatPHP
39-
*
40-
* @see FormatPHPWriter
37+
* using {@see FormatPHPWriter}
4138
*/
4239
class FormatPHPReader implements ReaderInterface
4340
{

src/Format/Reader/SimpleReader.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
use FormatPHP\Exception\InvalidMessageShapeException;
2626
use FormatPHP\Format\ReaderInterface;
27-
use FormatPHP\Format\Writer\SimpleWriter;
2827
use FormatPHP\Message;
2928
use FormatPHP\MessageCollection;
3029

@@ -35,9 +34,7 @@
3534

3635
/**
3736
* Returns a MessageCollection parsed from JSON-decoded data that was written
38-
* using Writer\Format\Simple
39-
*
40-
* @see SimpleWriter
37+
* using {@see SimpleWriter}
4138
*/
4239
class SimpleReader implements ReaderInterface
4340
{

src/Format/Reader/SmartlingReader.php

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,68 +22,21 @@
2222

2323
namespace FormatPHP\Format\Reader;
2424

25-
use FormatPHP\Exception\InvalidMessageShapeException;
26-
use FormatPHP\Format\ReaderInterface;
27-
use FormatPHP\Format\Writer\SmartlingWriter;
28-
use FormatPHP\Message;
2925
use FormatPHP\MessageCollection;
3026

31-
use function gettype;
32-
use function is_array;
33-
use function is_string;
34-
use function sprintf;
35-
3627
/**
3728
* Returns a MessageCollection parsed from JSON-decoded data that was written
38-
* using Writer\Format\Smartling
39-
*
40-
* @see SmartlingWriter
29+
* using {@see SmartlingWriter}
4130
*/
42-
class SmartlingReader implements ReaderInterface
31+
class SmartlingReader extends ChromeReader
4332
{
4433
/**
4534
* @inheritdoc
4635
*/
4736
public function __invoke(array $data): MessageCollection
4837
{
49-
$messages = new MessageCollection();
50-
5138
unset($data['smartling']);
5239

53-
/**
54-
* @var string $messageId
55-
* @var array{message: string} $message
56-
*/
57-
foreach ($data as $messageId => $message) {
58-
$this->validateShape($messageId, $message);
59-
60-
$messages[] = new Message($messageId, $message['message']);
61-
}
62-
63-
return $messages;
64-
}
65-
66-
/**
67-
* @param array-key $messageId
68-
* @param mixed $message
69-
*
70-
* @throws InvalidMessageShapeException
71-
*/
72-
private function validateShape($messageId, $message): void
73-
{
74-
if (!is_string($messageId)) {
75-
throw new InvalidMessageShapeException(sprintf(
76-
'%s expects a string message ID; received %s',
77-
self::class,
78-
gettype($messageId),
79-
));
80-
}
81-
82-
if (!is_array($message) || !is_string($message['message'] ?? null)) {
83-
throw new InvalidMessageShapeException(sprintf(
84-
'%s expects a string message property; message does not exist or is not a string',
85-
self::class,
86-
));
87-
}
40+
return parent::__invoke($data);
8841
}
8942
}

src/Format/Writer/ChromeWriter.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
/**
4+
* This file is part of skillshare/formatphp
5+
*
6+
* skillshare/formatphp is open source software: you can distribute
7+
* it and/or modify it under the terms of the MIT License
8+
* (the "License"). You may not use this file except in
9+
* compliance with the License.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14+
* implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*
17+
* @copyright Copyright (c) Skillshare, Inc. <https://www.skillshare.com>
18+
* @license https://opensource.org/licenses/MIT MIT License
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace FormatPHP\Format\Writer;
24+
25+
use FormatPHP\DescriptorCollection;
26+
use FormatPHP\Format\Reader\ChromeReader;
27+
use FormatPHP\Format\WriterInterface;
28+
use FormatPHP\Format\WriterOptions;
29+
30+
/**
31+
* Chrome formatter for FormatPHP
32+
*
33+
* This implements the Chrome JSON format.
34+
*
35+
* ```json
36+
* {
37+
* "my.message": {
38+
* "description": "And I'm providing more details for translators here."
39+
* "message": "This is a message for translation."
40+
* }
41+
* }
42+
* ```
43+
*
44+
* @link hhttps://developer.chrome.com/docs/extensions/mv3/i18n-messages/ Chrome JSON format
45+
* @see ChromeReader
46+
*/
47+
class ChromeWriter implements WriterInterface
48+
{
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function __invoke(DescriptorCollection $collection, WriterOptions $options): array
53+
{
54+
$format = [];
55+
56+
foreach ($collection as $item) {
57+
$message = [];
58+
59+
if ($item->getDescription() !== null) {
60+
$message['description'] = $item->getDescription();
61+
}
62+
63+
$message['message'] = $item->getDefaultMessage() ?? '';
64+
65+
$format[(string) $item->getId()] = $message;
66+
}
67+
68+
return $format;
69+
}
70+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/**
4+
* This file is part of skillshare/formatphp
5+
*
6+
* skillshare/formatphp is open source software: you can distribute
7+
* it and/or modify it under the terms of the MIT License
8+
* (the "License"). You may not use this file except in
9+
* compliance with the License.
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14+
* implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*
17+
* @copyright Copyright (c) Skillshare, Inc. <https://www.skillshare.com>
18+
* @license https://opensource.org/licenses/MIT MIT License
19+
*/
20+
21+
declare(strict_types=1);
22+
23+
namespace FormatPHP\Format\Writer;
24+
25+
/**
26+
* Crowdin formatter for FormatPHP
27+
*
28+
* This follows the same format as the Crowdin formatter for FormatJS and
29+
* implements the Crowdin Chrome JSON format.
30+
*
31+
* ```json
32+
* {
33+
* "my.message": {
34+
* "description": "And I'm providing more details for translators here."
35+
* "message": "This is a message for translation."
36+
* }
37+
* }
38+
* ```
39+
*
40+
* @link https://support.crowdin.com/file-formats/chrome-json/ Crowdin Chrome JSON format
41+
* @see CrowdinReader
42+
*/
43+
class CrowdinWriter extends ChromeWriter
44+
{
45+
}

0 commit comments

Comments
 (0)