Skip to content

Commit 57824f1

Browse files
committed
Initial functionality
1 parent af8be35 commit 57824f1

File tree

7 files changed

+346
-0
lines changed

7 files changed

+346
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/.idea/
2+
/vendor/
3+
.DS_Store
4+
composer.lock

README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,62 @@
22
JSON Translation Helper scans your project for `__()`, `lang()` translation helper methods and `@lang` directives, then it creates keys in your JSON translation files.
33

44
And yes, it avoids duplicates – helper creates only non-existing keys. Ready for your translation.
5+
6+
## Installation
7+
8+
First, install JSON Translation Helper using the Composer require command:
9+
10+
```
11+
composer require subotkevic/laravel-json-translation-helper
12+
```
13+
14+
That's it. Package registers service provider automatically.
15+
16+
## Usage
17+
18+
### Translation files
19+
20+
First, you have to create your translation files for languages you will translate your application to.
21+
22+
For example, if you want your application to have a Spanish translation, you should create a `resources/lang/es.json` file.
23+
24+
Of course you can have multiple translation files:
25+
```
26+
resources/
27+
lang/
28+
es.json
29+
fr.json
30+
```
31+
32+
Make sure that your translation files is valid JSON, otherwise our package will not work:
33+
34+
```
35+
{
36+
"I love programming.": "Me encanta programar."
37+
}
38+
```
39+
40+
If you don't have any translations for now, just **make sure your file is not empty**, but actually an empty JSON object:
41+
```
42+
{}
43+
```
44+
45+
### Scan your application
46+
47+
Finally, to scan your application for missing translation keys just run:
48+
49+
```
50+
php artisan translation:scan
51+
```
52+
53+
## Customization
54+
55+
You can change the default paths to scan your application from, the output directory where your JSON translation files are located, and even the file extensions you want to scan from.
56+
57+
First, publish the configuration file:
58+
59+
```
60+
php artisan vendor:publish --provider="JsonTranslationHelper\TranslationHelperServiceProvider"
61+
```
62+
63+
Then in the `config/translation-helper.php` you can change default values of `scan_directories`, `file_extensions` and `output_directory`.

composer.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "subotkevic/laravel-json-translation-helper",
3+
"description": "Searches for translation keys – inserts into JSON translation files.",
4+
"type": "project",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Artur Subotkevič",
9+
"email": "artur@subotkevic.com"
10+
}
11+
],
12+
"require": {
13+
"php": ">=7.0.0",
14+
"laravel/framework": ">=5.4.x-dev",
15+
"ext-json": "*"
16+
},
17+
"require-dev": {
18+
"phpunit/phpunit": "~6.0"
19+
},
20+
"autoload": {
21+
"psr-4": {
22+
"Translator\\": "src/"
23+
},
24+
"files": [
25+
"src/helpers.php"
26+
]
27+
},
28+
"autoload-dev": {
29+
"psr-4": {
30+
"JsonTranslationHelper\\Tests\\": "tests/"
31+
}
32+
},
33+
"extra": {
34+
"laravel": {
35+
"providers": [
36+
"JsonTranslationHelper\\TranslationHelperServiceProvider"
37+
]
38+
}
39+
}
40+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<?php
2+
3+
namespace JsonTranslationHelper\Command;
4+
5+
use Illuminate\Console\Command;
6+
7+
class TranslationHelperCommand extends Command
8+
{
9+
/**
10+
* @var string
11+
*/
12+
protected $signature = 'translation:scan';
13+
14+
/**
15+
* @var string
16+
*/
17+
protected $description = 'Searches for translation keys – inserts into JSON translation files.';
18+
19+
/**
20+
* Execute the console command.
21+
*
22+
* @return void
23+
*/
24+
public function handle()
25+
{
26+
$translationKeys = $this->findProjectTranslationsKeys();
27+
$translationFiles = $this->getProjectTranslationFiles();
28+
29+
foreach ($translationFiles as $file) {
30+
$translationData = $this->getAlreadyTranslatedKeys($file);
31+
$this->line("lang " . str_replace('.json', '', basename($file)));
32+
$added = [];
33+
34+
foreach ($translationKeys as $key) {
35+
if (!isset($translationData[$key])) {
36+
$this->warn(" - Added {$key}");
37+
$translationData[$key] = '';
38+
$added[] = $key;
39+
}
40+
}
41+
42+
if ($added) {
43+
$this->line("Updating translation file...");
44+
$this->writeNewTranslationFile($file, $translationData);
45+
$this->info("Done!");
46+
} else {
47+
$this->warn("Nothing new found for this language.");
48+
}
49+
$this->line("");
50+
}
51+
}
52+
53+
/**
54+
* @return array
55+
*/
56+
private function findProjectTranslationsKeys()
57+
{
58+
$allKeys = [];
59+
$viewsDirectories = config('translation-helper.views_directories');
60+
$fileExtensions = config('translation-helper.file_extensions');
61+
62+
foreach($viewsDirectories as $directory) {
63+
foreach ($fileExtensions as $extension) {
64+
$this->getTranslationKeysFromDir($allKeys, $directory, $extension);
65+
}
66+
}
67+
68+
ksort($allKeys);
69+
70+
return $allKeys;
71+
}
72+
73+
/**
74+
* @param array $keys
75+
* @param string $dirPath
76+
* @param string $fileExt
77+
*/
78+
private function getTranslationKeysFromDir(&$keys, $dirPath, $fileExt = 'php')
79+
{
80+
$files = glob_recursive("{$dirPath}/*.{$fileExt}", GLOB_BRACE);
81+
82+
foreach ($files as $file) {
83+
$content = $this->getSanitizedContent($file);
84+
85+
$this->getTranslationKeysFromFunction($keys, 'lang', $content);
86+
$this->getTranslationKeysFromFunction($keys, '__', $content);
87+
}
88+
}
89+
90+
/**
91+
* @param array $keys
92+
* @param string $functionName
93+
* @param string $content
94+
*/
95+
private function getTranslationKeysFromFunction(&$keys, $functionName, $content)
96+
{
97+
$matches = [];
98+
99+
preg_match_all("#{$functionName}\(\'(.*?)\'\)#", $content, $matches);
100+
101+
if ( ! empty($matches)) {
102+
foreach ($matches[1] as $match) {
103+
$match = str_replace('"', "'", str_replace("\'", "'", $match));
104+
105+
if ( ! empty($match)) {
106+
$keys[$match] = $match;
107+
}
108+
}
109+
}
110+
}
111+
112+
/**
113+
* @return array
114+
*/
115+
private function getProjectTranslationFiles()
116+
{
117+
$path = config('translation-helper.translations_output');
118+
$files = glob("{$path}/*.json", GLOB_BRACE);
119+
120+
return $files;
121+
}
122+
123+
/**
124+
* @param string $filePath
125+
* @return array
126+
*/
127+
private function getAlreadyTranslatedKeys($filePath)
128+
{
129+
$current = json_decode(file_get_contents($filePath), true);
130+
131+
ksort($current);
132+
133+
return $current;
134+
}
135+
136+
/**
137+
* @param string $filePath
138+
* @param array $translations
139+
*/
140+
private function writeNewTranslationFile($filePath, $translations)
141+
{
142+
file_put_contents($filePath, json_encode($translations, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
143+
}
144+
145+
/**
146+
* @param string $filePath
147+
* @return string
148+
*/
149+
private function getSanitizedContent($filePath)
150+
{
151+
return str_replace("\n", ' ', file_get_contents($filePath));
152+
}
153+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace JsonTranslationHelper;
4+
5+
use Illuminate\Support\ServiceProvider;
6+
7+
class TranslationHelperServiceProvider extends ServiceProvider
8+
{
9+
/**
10+
* Bootstrap the application services.
11+
*
12+
* @return void
13+
*/
14+
public function boot()
15+
{
16+
if ($this->app->runningInConsole()) {
17+
$this->commands([
18+
\JsonTranslationHelper\Command\TranslationHelperCommand::class,
19+
]);
20+
}
21+
22+
$this->mergeConfigFrom(__DIR__ . '/config/translation-helper.php', 'translation-helper');
23+
24+
$this->publishes([
25+
__DIR__ . '/config/translation-helper.php' => base_path('config/translation-helper.php'),
26+
], 'config');
27+
}
28+
}

src/config/translation-helper.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
return [
4+
/**
5+
* Directories to scan for missing translation keys.
6+
*/
7+
'scan_directories' => [
8+
app_path(),
9+
resource_path('views'),
10+
resource_path('assets'),
11+
],
12+
13+
/**
14+
* File extensions to scan from.
15+
*/
16+
'file_extensions' => [
17+
'php',
18+
//'js',
19+
//'vue',
20+
],
21+
22+
/**
23+
* Directory where your JSON translation files are located.
24+
*/
25+
'output_directory' => resource_path('lang'),
26+
];

src/helpers.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
if ( ! function_exists('lang')) {
4+
/**
5+
* Translate the given message.
6+
*
7+
* @param string $key
8+
* @param array $replace
9+
* @param string $locale
10+
* @return \Illuminate\Contracts\Translation\Translator|string
11+
*/
12+
function lang($key = null, $replace = [], $locale = null)
13+
{
14+
return __($key, $replace, $locale);
15+
}
16+
}
17+
18+
if ( ! function_exists('glob_recursive')) {
19+
/**
20+
* Find path names matching a pattern recursively
21+
*
22+
* @param $pattern
23+
* @param int $flags
24+
* @return array
25+
*/
26+
function glob_recursive($pattern, $flags = 0)
27+
{
28+
$files = glob($pattern, $flags);
29+
30+
foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
31+
$files = array_merge($files, glob_recursive($dir . '/' . basename($pattern), $flags));
32+
}
33+
34+
return $files;
35+
}
36+
}

0 commit comments

Comments
 (0)