├── .phpunit-watcher.yml
├── config
├── di-console.php
└── params.php
├── infection.json.dist
├── psalm.xml
├── rector.php
├── CHANGELOG.md
├── src
├── Exception
│ └── NoCategorySourceConfigException.php
├── CategorySource.php
├── Command
│ └── ExtractCommand.php
└── Extractor.php
├── LICENSE.md
├── .styleci.yml
├── composer.json
└── README.md
/.phpunit-watcher.yml:
--------------------------------------------------------------------------------
1 | watch:
2 | directories:
3 | - src
4 | - tests
5 | fileMask: '*.php'
6 | notifications:
7 | passingTests: false
8 | failingTests: false
9 | phpunit:
10 | binaryPath: vendor/bin/phpunit
11 | timeout: 180
12 |
--------------------------------------------------------------------------------
/config/di-console.php:
--------------------------------------------------------------------------------
1 | [
11 | '__construct()' => [
12 | [],
13 | ],
14 | ],
15 | ];
16 |
--------------------------------------------------------------------------------
/config/params.php:
--------------------------------------------------------------------------------
1 | [
9 | 'commands' => [
10 | 'translator/extract' => ExtractCommand::class,
11 | ],
12 | ],
13 | ];
14 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src"
5 | ]
6 | },
7 | "logs": {
8 | "text": "php:\/\/stderr",
9 | "stryker": {
10 | "report": "master"
11 | }
12 | },
13 | "mutators": {
14 | "@default": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
12 | __DIR__ . '/src',
13 | __DIR__ . '/tests',
14 | ]);
15 |
16 | // register a single rule
17 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
18 |
19 | // define sets of rules
20 | $rectorConfig->sets([
21 | LevelSetList::UP_TO_PHP_80,
22 | ]);
23 |
24 | $rectorConfig->skip([
25 | ClosureToArrowFunctionRector::class,
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Yii Translator Message Extractor Change Log
2 |
3 | ## 2.1.2 under development
4 |
5 | - no changes in this release.
6 |
7 | ## 2.1.1 February 13, 2024
8 |
9 | - Enh #47: Add "dev" keyword to composer.json for suggest package to require-dev section (@samdark)
10 |
11 | ## 2.1.0 January 18, 2024
12 |
13 | - Enh #42: Add ability to change the calling method in translator extractor (@terabytesoftw)
14 | - Enh #43, #45: Add support of `symfony/console` version `^7.0` (@terabytesoftw, @vjik)
15 |
16 | ## 2.0.0 February 17, 2023
17 |
18 | - Chg #31: Adapt configuration group names to Yii conventions (@vjik)
19 | - Chg #34: Make `yiisoft/yii-console` dependency for development only (@vjik)
20 | - Enh #31: Improve solution in `NoCategorySourceConfigException` (@vjik)
21 | - Enh #33: Add support of `yiisoft/translator` version `^3.0` (@vjik)
22 |
23 | ## 1.1.0 November 08, 2022
24 |
25 | - Enh #18: Raise minimum PHP version to `^8.0` (@vjik, @xepozz)
26 | - Enh #20: Add support of `yiisoft/translator` version `^2.0` (@vjik)
27 |
28 | ## 1.0.0 December 20, 2021
29 |
30 | - Initial release.
31 |
--------------------------------------------------------------------------------
/src/Exception/NoCategorySourceConfigException.php:
--------------------------------------------------------------------------------
1 | [
29 | '__construct()' => [
30 | DynamicReferencesArray::from([
31 | static fn (Aliases $aliases) => new MessageSource($aliases->get('@message')),
32 | ])
33 | ],
34 | ],
35 | ];
36 | ```
37 | SOLUTION;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software (https://www.yiiframework.com/)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/src/CategorySource.php:
--------------------------------------------------------------------------------
1 | name;
37 | }
38 |
39 | /**
40 | * @param string $category Category of messages to get.
41 | * @param string $locale Locale of messages to get.
42 | *
43 | * @psalm-return array>
44 | *
45 | * @return array All messages from category. The format is
46 | * the same as in {@see \Yiisoft\Translator\MessageReaderInterface::getMessages()}.
47 | */
48 | public function readMessages(string $category, string $locale): array
49 | {
50 | return $this->reader->getMessages($category, $locale);
51 | }
52 |
53 | /**
54 | * @param string $category Category to write messages to.
55 | * @param string $locale Locale to write messages for.
56 | * @param array $messages Messages to write.
57 | *
58 | * @psalm-param array> $messages
59 | *
60 | * @see \Yiisoft\Translator\MessageWriterInterface::write
61 | */
62 | public function writeMessages(string $category, string $locale, array $messages): void
63 | {
64 | $this->writer->write($category, $locale, $messages);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: psr12
2 | risky: true
3 |
4 | version: 8.1
5 |
6 | finder:
7 | exclude:
8 | - docs
9 | - vendor
10 |
11 | enabled:
12 | - alpha_ordered_traits
13 | - array_indentation
14 | - array_push
15 | - combine_consecutive_issets
16 | - combine_consecutive_unsets
17 | - combine_nested_dirname
18 | - declare_strict_types
19 | - dir_constant
20 | - fully_qualified_strict_types
21 | - function_to_constant
22 | - hash_to_slash_comment
23 | - is_null
24 | - logical_operators
25 | - magic_constant_casing
26 | - magic_method_casing
27 | - method_separation
28 | - modernize_types_casting
29 | - native_function_casing
30 | - native_function_type_declaration_casing
31 | - no_alias_functions
32 | - no_empty_comment
33 | - no_empty_phpdoc
34 | - no_empty_statement
35 | - no_extra_block_blank_lines
36 | - no_short_bool_cast
37 | - no_superfluous_elseif
38 | - no_unneeded_control_parentheses
39 | - no_unneeded_curly_braces
40 | - no_unneeded_final_method
41 | - no_unset_cast
42 | - no_unused_imports
43 | - no_unused_lambda_imports
44 | - no_useless_else
45 | - no_useless_return
46 | - normalize_index_brace
47 | - php_unit_dedicate_assert
48 | - php_unit_dedicate_assert_internal_type
49 | - php_unit_expectation
50 | - php_unit_mock
51 | - php_unit_mock_short_will_return
52 | - php_unit_namespaced
53 | - php_unit_no_expectation_annotation
54 | - phpdoc_no_empty_return
55 | - phpdoc_no_useless_inheritdoc
56 | - phpdoc_order
57 | - phpdoc_property
58 | - phpdoc_scalar
59 | - phpdoc_singular_inheritdoc
60 | - phpdoc_trim
61 | - phpdoc_trim_consecutive_blank_line_separation
62 | - phpdoc_type_to_var
63 | - phpdoc_types
64 | - phpdoc_types_order
65 | - print_to_echo
66 | - regular_callable_call
67 | - return_assignment
68 | - self_accessor
69 | - self_static_accessor
70 | - set_type_to_cast
71 | - short_array_syntax
72 | - short_list_syntax
73 | - simplified_if_return
74 | - single_quote
75 | - standardize_not_equals
76 | - ternary_to_null_coalescing
77 | - trailing_comma_in_multiline_array
78 | - unalign_double_arrow
79 | - unalign_equals
80 | - empty_loop_body_braces
81 | - integer_literal_case
82 | - union_type_without_spaces
83 |
84 | disabled:
85 | - function_declaration
86 |
--------------------------------------------------------------------------------
/src/Command/ExtractCommand.php:
--------------------------------------------------------------------------------
1 | addOption('languages', 'L', InputOption::VALUE_OPTIONAL, 'Comma separated list of languages to write message sources for. By default it is `en`.', 'en')
32 | ->addOption('category', 'C', InputOption::VALUE_OPTIONAL, 'Default message category to use when category is not set.', $this->defaultCategory)
33 | ->addOption('except', 'E', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Exclude path from extracting.', [])
34 | ->addOption('only', 'O', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Use the only specified path for extracting.', [])
35 | ->addArgument('path', InputArgument::OPTIONAL, 'Path for extracting message IDs.')
36 | ->setHelp('This command Extracts translator IDs from files within a given path.');
37 | }
38 |
39 | protected function execute(InputInterface $input, OutputInterface $output): int
40 | {
41 | /** @var string */
42 | $path = $input->getArgument('path') ?? getcwd();
43 |
44 | /** @var string */
45 | $languages = $input->getOption('languages');
46 |
47 | /** @var string */
48 | $category = $input->getOption('category');
49 |
50 | /** @var string[] */
51 | $except = $input->getOption('except');
52 |
53 | /** @var string[] */
54 | $only = $input->getOption('only');
55 |
56 | /** @var string[] */
57 | $languagesList = explode(',', $languages);
58 |
59 | $this->extractor->setExcept($except);
60 | $this->extractor->setOnly($only);
61 |
62 | $this->extractor->process($path, $category, $languagesList, $output);
63 |
64 | return 0;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiisoft/translator-extractor",
3 | "type": "library",
4 | "description": "Yii message extractor",
5 | "keywords": [
6 | "i18n",
7 | "internationalization",
8 | "extractor",
9 | "dev"
10 | ],
11 | "homepage": "https://www.yiiframework.com/",
12 | "license": "BSD-3-Clause",
13 | "support": {
14 | "issues": "https://github.com/yiisoft/translator-extractor/issues?state=open",
15 | "source": "https://github.com/yiisoft/translator-extractor",
16 | "forum": "https://www.yiiframework.com/forum/",
17 | "wiki": "https://www.yiiframework.com/wiki/",
18 | "irc": "ircs://irc.libera.chat:6697/yii",
19 | "chat": "https://t.me/yii3en"
20 | },
21 | "funding": [
22 | {
23 | "type": "opencollective",
24 | "url": "https://opencollective.com/yiisoft"
25 | },
26 | {
27 | "type": "github",
28 | "url": "https://github.com/sponsors/yiisoft"
29 | }
30 | ],
31 | "require": {
32 | "php": "^8.0",
33 | "symfony/console": "^5.4|^6.0|^7.0",
34 | "yiisoft/friendly-exception": "^1.0",
35 | "yiisoft/translator": "^1.0|^2.0|^3.0"
36 | },
37 | "require-dev": {
38 | "maglnet/composer-require-checker": "^4.2",
39 | "phpunit/phpunit": "^9.5",
40 | "rector/rector": "^1.0",
41 | "roave/infection-static-analysis-plugin": "^1.16",
42 | "spatie/phpunit-watcher": "^1.23",
43 | "vimeo/psalm": "^4.30|^5.6",
44 | "yiisoft/di": "^1.0",
45 | "yiisoft/yii-console": "^2.0"
46 | },
47 | "autoload": {
48 | "psr-4": {
49 | "Yiisoft\\TranslatorExtractor\\": "src"
50 | }
51 | },
52 | "autoload-dev": {
53 | "psr-4": {
54 | "Yiisoft\\TranslatorExtractor\\Tests\\": "tests"
55 | }
56 | },
57 | "extra": {
58 | "config-plugin-options": {
59 | "source-directory": "config"
60 | },
61 | "config-plugin": {
62 | "params": "params.php",
63 | "di-console": "di-console.php"
64 | }
65 | },
66 | "config": {
67 | "sort-packages": true,
68 | "allow-plugins": {
69 | "infection/extension-installer": true,
70 | "composer/package-versions-deprecated": true
71 | }
72 | },
73 | "scripts": {
74 | "mutation": [
75 | "Composer\\Config::disableProcessTimeout",
76 | "roave-infection-static-analysis-plugin"
77 | ],
78 | "psalm": "psalm",
79 | "test": "phpunit --testdox --no-interaction",
80 | "test-watch": "phpunit-watcher watch"
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Extractor.php:
--------------------------------------------------------------------------------
1 | translate')
34 | {
35 | if (empty($categories)) {
36 | throw new NoCategorySourceConfigException();
37 | }
38 |
39 | foreach ($categories as $category) {
40 | $this->categorySources[$category->getName()] = $category;
41 | }
42 | }
43 |
44 | /**
45 | * Set list of patterns that the files or directories should not match.
46 | *
47 | * @see PathMatcher
48 | *
49 | * @param string[] $except
50 | */
51 | public function setExcept(array $except): void
52 | {
53 | if (!empty($except)) {
54 | $this->except = $except;
55 | }
56 | }
57 |
58 | /**
59 | * Set list of patterns that the files or directories should match.
60 | *
61 | * @see PathMatcher
62 | *
63 | * @param string[] $only
64 | */
65 | public function setOnly(array $only): void
66 | {
67 | if (!empty($only)) {
68 | $this->only = $only;
69 | }
70 | }
71 |
72 | /**
73 | * @param string $filesPath Path to files to extract from.
74 | * @param string $defaultCategory Category to use if category isn't set in translation call.
75 | * @param string[] $languages Languages to write extracted IDs to.
76 | */
77 | public function process(string $filesPath, string $defaultCategory, array $languages, OutputInterface $output): void
78 | {
79 | if (!isset($this->categorySources[$defaultCategory])) {
80 | $output->writeln('Default category was not found in a list of Categories.');
81 | return;
82 | }
83 |
84 | $translationExtractor = new TranslationExtractor(
85 | $filesPath,
86 | $this->applyRoot($this->only, $filesPath),
87 | $this->applyRoot($this->except, $filesPath)
88 | );
89 |
90 | $messagesList = $translationExtractor->extract($defaultCategory, $this->translatorCall);
91 |
92 | if (empty($messagesList)) {
93 | $output->writeln('Messages not found');
94 | return;
95 | }
96 |
97 | $output->writeln('Languages: ' . implode(', ', $languages));
98 |
99 | /**
100 | * @var string $categoryName
101 | * @var array|mixed> $messages
102 | */
103 | foreach ($messagesList as $categoryName => $messages) {
104 | $output->writeln('Category: "' . $categoryName . '", messages found: ' . count($messages) . '.');
105 |
106 | /** @var array> $convertedMessages */
107 | $convertedMessages = $this->convert($messages);
108 | foreach ($languages as $language) {
109 | $extractCategory = isset($this->categorySources[$categoryName]) ? $categoryName : $defaultCategory;
110 | $this->addMessages($extractCategory, $language, $convertedMessages);
111 | }
112 | }
113 | }
114 |
115 | private function addMessages(string $categoryName, string $language, array $messages): void
116 | {
117 | $readMessages = $this->categorySources[$categoryName]->readMessages($categoryName, $language);
118 | /** @var array> $convertedMessages */
119 | $convertedMessages = array_merge($messages, $readMessages);
120 | $this->categorySources[$categoryName]->writeMessages($categoryName, $language, $convertedMessages);
121 | }
122 |
123 | private function convert(array $messages): array
124 | {
125 | $returningMessages = [];
126 |
127 | /** @var array $messages */
128 | foreach ($messages as $message) {
129 | $returningMessages[$message] = ['message' => $message];
130 | }
131 |
132 | return $returningMessages;
133 | }
134 |
135 | /**
136 | * @param string[]|null $list
137 | *
138 | * @return string[]|null
139 | */
140 | private function applyRoot(?array $list, string $rootFolder): ?array
141 | {
142 | if (is_array($list)) {
143 | return array_map(
144 | static fn (string $except): string => preg_replace('#^\./#', $rootFolder . '/', $except),
145 | $list
146 | );
147 | }
148 |
149 | return $list;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Yii Translator Message Extractor
7 |
8 | [](https://packagist.org/packages/yiisoft/translator-extractor)
9 | [](https://packagist.org/packages/yiisoft/translator-extractor)
10 | [](https://github.com/yiisoft/translator-extractor/actions?query=workflow%3Abuild)
11 | [](https://scrutinizer-ci.com/g/yiisoft/translator-extractor/?branch=master)
12 | [](https://scrutinizer-ci.com/g/yiisoft/translator-extractor/?branch=master)
13 | [](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/translator-extractor/master)
14 | [](https://github.com/yiisoft/translator-extractor/actions?query=workflow%3A%22static+analysis%22)
15 | [](https://shepherd.dev/github/yiisoft/translator-extractor)
16 |
17 | The package allows automatically extracting translation IDs from PHP source files and writing them to
18 | [one of the translator message sources](https://github.com/yiisoft/translator#message-sources).
19 |
20 | ## Requirements
21 |
22 | - PHP 8.0 or higher.
23 |
24 | ## Installation
25 |
26 | The package could be installed with [Composer](https://getcomposer.org):
27 |
28 | ```shell
29 | composer require yiisoft/translator-extractor
30 | ```
31 |
32 | ## Configuration
33 |
34 | You need configure `MessageReader` and `MessageWriter` in config file of the package, `config/console/translator-extractor.php`:
35 |
36 | For example, when using PHP `MessageSource` the config will be the following using **relative path**:
37 |
38 | ```php
39 | use \Yiisoft\Translator\Message\Php\MessageSource;
40 |
41 | return [
42 | Extractor::class => [
43 | '__construct()' => [
44 | [
45 | DynamicReference::to([
46 | 'class' => ExtractorCategorySource::class,
47 | '__construct()' => [
48 | 'app',
49 | 'messageReader' => DynamicReference::to(static fn () => new MessageSource($params['yiisoft/translator-extractor']['messagePath'])),
50 | 'messageWriter' => DynamicReference::to(static fn () => new MessageSource($params['yiisoft/translator-extractor']['messagePath'])),
51 | ],
52 | ]),
53 | ],
54 | ],
55 | '->translate' // optional, default value for Translation call to look for.
56 | ],
57 | ];
58 | ```
59 |
60 | And in `params.php` file you can configure parameters of a message source:
61 |
62 | ```php
63 | return [
64 | 'yiisoft/yii-console' => [
65 | 'commands' => [
66 | 'translator/extract' => ExtractCommand::class,
67 | ],
68 | ],
69 | 'yiisoft/translator-extractor' => [
70 | // Using relative path:
71 | 'messagePath' => dirname(__DIR__, 5) . '/messages',
72 | ],
73 | ];
74 | ```
75 |
76 | Or if with using PHP `MessageSource` the config will be the following **using Aliases**:
77 |
78 | ```php
79 | use \Yiisoft\Translator\Message\Php\MessageSource;
80 |
81 | return [
82 | Extractor::class => [
83 | '__construct()' => [
84 | [
85 | DynamicReference::to([
86 | 'class' => ExtractorCategorySource::class,
87 | '__construct()' => [
88 | 'app',
89 | 'messageReader' => DynamicReference::to(static fn (Aliases $aliases) => new MessageSource($aliases->get('@message'))),
90 | 'messageWriter' => DynamicReference::to(static fn (Aliases $aliases) => new MessageSource($aliases->get('@message'))),
91 | ],
92 | ]),
93 | ],
94 | ],
95 | ],
96 | ];
97 | ```
98 |
99 | > **Attention**: Both `MessageReader` and `MessageWriter` should be configured for using _the same_ `MessageSource`. The extractor needs it to work with existing messages.
100 |
101 | ## General usage
102 |
103 | ```shell
104 | ./yii translator/extract
105 | ```
106 |
107 | This command will recursively find all messages in the code starting with the current directory and will save it into
108 | a message source for default language `en`. You can specify the path explicitly:
109 |
110 | ```shell
111 | ./yii translator/extract /path/to/your/project
112 | ```
113 |
114 | **Notice:** By default extractor has `vendor` directory in the application directory excluded. To include it you can specify empty value for `except`:
115 |
116 | ```shell
117 | ./yii translator/extract /path/to/your/project --except=''
118 | ```
119 |
120 | Full list of options:
121 |
122 | ```shell
123 | Usage:
124 | translator/extract [options] [--] []
125 |
126 | Arguments:
127 | path Path for extracting message IDs.
128 |
129 | Options:
130 | -L, --languages=LANGUAGES Comma separated list of languages to write message sources for. By default it is `en`. [default: "en"]
131 | -C, --category=CATEGORY Default message category to use when category is not set. [default: "app"]
132 | -E, --except[=EXCEPT] Exclude path from extracting. (multiple values allowed)
133 | -O, --only[=ONLY] Use the only specified path for extracting. (multiple values allowed)
134 |
135 | ```
136 |
137 | ### Specify languages
138 |
139 | You can specify multiple languages to write IDs into:
140 |
141 | ```shell
142 | ./yii translator/extract --languages=en,ru
143 | ```
144 |
145 | Or in short format:
146 |
147 | ```shell
148 | ./yii translator/extract -Lru
149 | ```
150 |
151 | ### Specify default category
152 |
153 | Also, you can specify default message category to use when category is not set.
154 |
155 | ```shell
156 | ./yii translator/extract --category=your_category_name
157 | ```
158 |
159 | ### Using `except` option
160 |
161 | To exclude all directories named `dir1` use `--except`:
162 |
163 | ```shell
164 | ./yii translator/extract --except=**/dir1/**
165 | ```
166 |
167 | To exclude both `vendor` and `tests` directories the following options could be used:
168 |
169 | ```shell
170 | ./yii translator/extract --except=./vendor/** --except=./tests/**
171 | ```
172 |
173 | ### Using `only` option
174 |
175 | To parse only `test.php` files in any directory use `--only` option:
176 |
177 | ```shell
178 | ./yii translator/extract --only=**/test.php
179 | ```
180 |
181 | To parse only `/var/www/html/test.php` file use:
182 |
183 | ```shell
184 | ./yii translator/extract --only=/var/www/html/test.php
185 | ```
186 |
187 | For more info about `except` and `only` parameters check documentation of
188 | [yiisoft/files package](https://github.com/yiisoft/files).
189 |
190 | ## Working with gettext
191 |
192 | The package currently does not support extracting messages into gettext format. To extract messages for gettext,
193 | you may use the following shell script (in Linux-based OS):
194 |
195 | ```shell
196 | find src/ -name *.php | xargs xgettext --from-code=utf-8 --language=PHP --no-location --omit-header --sort-output --keyword=translate --output="locales/category.pot"
197 |
198 | for d in locales/*/ ; do
199 | for i in locales/*.pot; do
200 | if [ ! -f "$d$(basename "$i" .pot).po" ]; then
201 | touch "$d$(basename "$i" .pot).po"
202 | fi
203 |
204 | msgmerge --update --silent --backup=off "$d$(basename "$i" .pot).po" $i
205 | done
206 | done
207 | ```
208 |
209 | ## Documentation
210 |
211 | - [Internals](docs/internals.md)
212 |
213 | If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that.
214 | You may also check out other [Yii Community Resources](https://www.yiiframework.com/community).
215 |
216 | ## License
217 |
218 | The Yii Translator Message Extractor is free software. It is released under the terms of the BSD License. Please
219 | see [`LICENSE`](./LICENSE.md) for more information.
220 |
221 | Maintained by [Yii Software](https://www.yiiframework.com/).
222 |
223 | ## Support the project
224 |
225 | [](https://opencollective.com/yiisoft)
226 |
227 | ## Follow updates
228 |
229 | [](https://www.yiiframework.com/)
230 | [](https://twitter.com/yiiframework)
231 | [](https://t.me/yii3en)
232 | [](https://www.facebook.com/groups/yiitalk)
233 | [](https://yiiframework.com/go/slack)
234 |
--------------------------------------------------------------------------------