Skip to content

Commit ad9f48d

Browse files
committed
html validation and xlsx support
1 parent 943c394 commit ad9f48d

File tree

6 files changed

+71
-54
lines changed

6 files changed

+71
-54
lines changed

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Laravel-Lang-Import-Export **(Refactored)**
44
==========================
55

6-
This package provides artisan commands to import and export language files from and to CSV. This can be used to send translations to agencies that normally work with Excel-like files. In practice, CSV format is supper easy to work with for any translator in Fiverr or for any other freelancer. Personally, I have tried every other format such as **php, yaml, docx, pod, txt** and all of them has too complex syntax and requires custom software to work with (Not to mention all those problems with file encodings). CSV solves it all!
6+
This package provides artisan commands to import and export language files from and to CSV. This can be used to send translations to agencies that normally work with Excel-like files. In practice, CSV format is supper easy to work with for any translator in Fiverr or for any other freelancer. Personally, I have tried every other format such as **php, yaml, docx, pod, txt** and all of them has too complex syntax and requires custom software to work with (Not to mention all those problems with file encodings). CSV solves it all! (Now supports **xls, xlsx, ods** file types too!)
77

88
# How It Works?
99

@@ -81,7 +81,7 @@ php artisan lang:import es.csv -p # validate imported translations for missing p
8181

8282
### Validate
8383
```bash
84-
php artisan lang:validate ar -m -v
84+
php artisan lang:validate ar -m --html -v
8585
```
8686
![Laravel-Lang-Import-Export validation example](https://raw.githubusercontent.com/AidasK/laravel-lang-import-export/master/validation.png)
8787

@@ -97,6 +97,10 @@ php artisan vendor:publish
9797
Changelog
9898
------------
9999

100+
6.2.0
101+
* Validate HTML feature. Usually HTML tags are translated with random spaces such as "< /b>", which makes entire paragraph bold.
102+
* Added support to import from xls, ods, xlsx, csv file types (PhpOffice integration)
103+
100104
6.1.0
101105
* Validate placeholders feature
102106

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"license": "MIT",
2020
"require": {
2121
"php": ">=7.0.0",
22-
"illuminate/support": "5.x"
22+
"illuminate/support": "5.x",
23+
"phpoffice/phpspreadsheet": "dev-master"
2324
},
2425
"autoload": {
2526
"psr-4": {

src/Console/ImportFromCsvCommand.php

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ class ImportFromCsvCommand extends Command
1919
{input : Filename of file to be imported with translation files.}
2020
{--l|locale= : The locale to be imported (default - parsed from file name).}
2121
{--g|group= : The name of translation file to imported (default - base group from config).}
22-
{--p|placeholders : Search for missing placeholders in imported keys.}
23-
{--column-map= : Map columns if other columns are used for notes (e.g. "0,1,3").}
24-
{--D|delimiter=, : Field delimiter (optional, default - ",").}
25-
{--E|enclosure=" : Field enclosure (optional, default - \'"\').}
26-
{--escape=" : Field escape (optional, default - \'"\').}
27-
{--X|excel : Set file encoding from Excel (optional, default - UTF-8).}';
22+
{--p|placeholders : Search for missing placeholders in imported keys (see config file for default value).}
23+
{--html : Validate html in imported keys (see config file for default value).}
24+
{--column-map= : Map columns if other columns are used for notes (e.g. "A,B,D").}
25+
';
2826

2927
/**
3028
* The console command description.
@@ -49,10 +47,18 @@ public function handle()
4947
$translations = $this->readTranslations($fileName);
5048
$group = $this->option('group');
5149
LangListService::writeLangList($locale, $group, $translations);
52-
if ($this->option('placeholders')) {
50+
if ($this->option('placeholders') || config('lang_import_export.import_validate_placeholders')) {
5351
$baseTranslations = LangListService::loadLangList(config('lang_import_export.base_locale'), $group);
5452
foreach (LangListService::validatePlaceholders($translations, $baseTranslations) as $errors) {
55-
$this->warn($locale . "/{$errors['group']}.{$errors['key']} is missing \"{$errors['placeholder']}\".");
53+
$this->warn("resources/lang/$locale/{$errors['group']}.php {$errors['key']} is missing \"{$errors['placeholder']}\".");
54+
$this->info($errors['translation'], 'v');
55+
$this->info($errors['baseTranslation'], 'vv');
56+
}
57+
}
58+
if ($this->option('html') || config('lang_import_export.import_validate_html')) {
59+
$baseTranslations = LangListService::loadLangList(config('lang_import_export.base_locale'), $group);
60+
foreach (LangListService::validateHTML($translations, $baseTranslations) as $errors) {
61+
$this->warn("resources/lang/$locale/{$errors['group']}.php {$errors['key']} is missing `{$errors['tag']}` html tag.");
5662
$this->info($errors['translation'], 'v');
5763
$this->info($errors['baseTranslation'], 'vv');
5864
}
@@ -68,38 +74,16 @@ public function handle()
6874
*/
6975
private function readTranslations($fileName)
7076
{
71-
if (($input = fopen($fileName, 'r')) === false) {
72-
throw new \Exception('File can not be opened ' . $fileName);
73-
}
74-
$translations = $this->readFile($input);
75-
fclose($input);
76-
77-
return $translations;
78-
}
79-
80-
/**
81-
* Read content of file.
82-
*
83-
* @param resource $input
84-
* @return array
85-
* @throws \Exception
86-
*/
87-
private function readFile($input)
88-
{
89-
if ($this->option('excel')) {
90-
$this->adjustFromExcel();
91-
}
77+
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($fileName);
78+
$rows = $spreadsheet->getActiveSheet()->toArray(null, false, false, true);
9279

9380
$translations = [];
94-
$confirmed = false;
95-
$map = explode(',', $this->option('column-map') ?: '0,1,2');
96-
while (($data = fgetcsv(
97-
$input, 0, $this->option('delimiter'), $this->option('enclosure'), $this->option('escape'))
98-
) !== false) {
99-
if (isset($translations[$data[0]]) == false) {
100-
$translations[$data[0]] = [];
81+
$map = explode(',', $this->option('column-map') ?: 'A,B,C');
82+
foreach ($rows as $data) {
83+
if (isset($translations[$data[$map[0]]]) == false) {
84+
$translations[$data[$map[0]]] = [];
10185
}
102-
$columns = sizeof($data);
86+
$columns = count($data);
10387
if ($columns < 3) {
10488
throw new \Exception("File has only $columns column/s");
10589
}
@@ -111,15 +95,4 @@ private function readFile($input)
11195

11296
return $translations;
11397
}
114-
115-
/**
116-
* Adjust file to Excel format.
117-
*
118-
* @return void
119-
*/
120-
private function adjustFromExcel()
121-
{
122-
$data = file_get_contents($this->argument('input'));
123-
file_put_contents($this->argument('input'), mb_convert_encoding($data, 'UTF-8', 'UTF-16'));
124-
}
12598
}

src/Console/ValidationCommand.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class ValidationCommand extends Command
1919
{target? : Locale to be checked.}
2020
{--l|locale= : Base locale (default - base locale from config).}
2121
{--g|group= : The name of translation file to export (default - base group from config).}
22+
{--html : Compare HTML}
2223
{--m|missing : Show missing translations}
2324
';
2425

@@ -47,6 +48,9 @@ public function handle()
4748
foreach ($this->strToArray($target) as $locale) {
4849
$targetTranslations = LangListService::loadLangList($locale, $groups);
4950
$this->validatePlaceholders($targetTranslations, $baseTranslations, $locale);
51+
if ($this->option('html')) {
52+
$this->validateHTML($targetTranslations, $baseTranslations, $locale);
53+
}
5054
if ($this->option('missing')) {
5155
$this->showMissing($targetTranslations, $baseTranslations, $locale);
5256
}
@@ -70,7 +74,22 @@ private function validatePlaceholders($targetTranslations, $baseTranslations, $l
7074
{
7175
$this->info('Searching for missing placeholers...');
7276
foreach (LangListService::validatePlaceholders($targetTranslations, $baseTranslations) as $errors) {
73-
$this->warn($locale . "/{$errors['group']}.{$errors['key']} is missing \"{$errors['placeholder']}\".");
77+
$this->warn("resources/lang/$locale/{$errors['group']}.php {$errors['key']} is missing \"{$errors['placeholder']}\".");
78+
$this->info($errors['translation'], 'v');
79+
$this->info($errors['baseTranslation'], 'vv');
80+
}
81+
}
82+
83+
/**
84+
* @param $targetTranslations
85+
* @param $baseTranslations
86+
* @param $locale
87+
*/
88+
private function validateHTML($targetTranslations, $baseTranslations, $locale)
89+
{
90+
$this->info('Searching for HTML differences...');
91+
foreach (LangListService::validateHTML($targetTranslations, $baseTranslations) as $errors) {
92+
$this->warn("resources/lang/$locale/{$errors['group']}.php {$errors['key']} is missing \"{$errors['tag']}\" html tag.");
7493
$this->info($errors['translation'], 'v');
7594
$this->info($errors['baseTranslation'], 'vv');
7695
}
@@ -81,12 +100,12 @@ private function showMissing($targetTranslations, $baseTranslations, $locale)
81100
$this->info('Searching for missing keys...');
82101
foreach ($baseTranslations as $group => $translations) {
83102
if (!isset($targetTranslations[$group])) {
84-
$this->warn("$locale/$group entire group is missing");
103+
$this->warn("resources/lang/$locale/$group.php entire group is missing");
85104
continue;
86105
}
87106
foreach ($translations as $key => $translation) {
88107
if (!empty($baseTranslations[$group][$key]) && !isset($targetTranslations[$group][$key])) {
89-
$this->warn("$locale/$group.$key is missing");
108+
$this->warn("resources/lang/$locale/$group.php $key is missing");
90109
}
91110
}
92111
}

src/LangListService.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,24 @@ public function validatePlaceholders($targetTranslations, $baseTranslations)
160160
}
161161
}
162162

163+
public function validateHTML($targetTranslations, $baseTranslations)
164+
{
165+
foreach ($targetTranslations as $group => $translations) {
166+
foreach ($translations as $key => $translation) {
167+
if (isset($baseTranslations[$group][$key]) && is_string($baseTranslations[$group][$key])) {
168+
$baseTranslation = $baseTranslations[$group][$key];
169+
preg_match_all('~(</?[a-z]+[^>]*?>)~i', $translation, $m);
170+
$tags = $m[1];
171+
foreach ($tags as $tag) {
172+
if (strpos($translation, $tag) === false) {
173+
yield compact('group', 'key', 'translation', 'baseTranslation', 'tag');
174+
}
175+
}
176+
}
177+
}
178+
}
179+
}
180+
163181
private function matchPlaceholders($translation)
164182
{
165183
preg_match_all('~(:[a-zA-Z0-9_]+)~', $translation, $m);

src/config/lang_import_export.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@
88
'export_target' => null,// target locale list separated by comma
99
'base_group' => null, // all groups or comma separated list
1010
'export_path' => storage_path(':locale:target.csv'),
11+
'import_validate_placeholders' => false, // validate placeholders by default
12+
'import_validate_html' => false, // validate html by default
1113
];

0 commit comments

Comments
 (0)