Skip to content
69 changes: 50 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,56 @@ composer require coderello/laravel-nova-lang

## Usage
### Publish Command
Publish translations for one language:
```bash
php artisan nova-lang:publish de
```
* Publish translations for one language:
```bash
php artisan nova-lang:publish de
```

Publish translations for multiple languages:
```bash
php artisan nova-lang:publish de,ru
```
* Publish translations for multiple languages (comma-separated):
```bash
php artisan nova-lang:publish de,ru
```

Publish translations for all languages:
```bash
php artisan nova-lang:publish --all
```
* Publish translations for all available languages:
```bash
php artisan nova-lang:publish --all
```

Publish translations and override existing files:
```bash
php artisan nova-lang:publish de,ru --force
```
* Publish translations and override existing files:
```bash
php artisan nova-lang:publish de,ru --force
```

#### Aliases
The language codes chosen for the files in this repository may not match the preferences for your project. You can use the `‑‑alias` option to publish locales using a different filename.

* Publish translations for one language with an alias, using the simple format `{alias}`:
```bash
php artisan nova-lang:publish de --alias=de-DE
```
This will publish the file `de-DE.json`.

* Publish translations for multiple languages with multiple aliases, using the format `{locale}:{alias}` (comma-separated):
```bash
php artisan nova-lang:publish de,ru,fr --alias=de:de-DE,ru:ru-RU
```
This will publish the files `de-DE.json`, `ru-RU.json` and `fr.json` (no alias).

* Aliases can also be used with the `--all` flag:

```bash
php artisan nova-lang:publish --all --alias=es:es-ES
```
You do not need to supply an alias for every locale that is to be published, only those that you wish to override.

* Here are some example aliases for common use cases:

* Use Chinese with scripts instead of regions: `zh-CN:zh-Hans,zh-TW:zh-Hant`
* Default to Brazilian Portuguese over European: `pt:pt-PT,pt-BR:pt`
* Default to Serbian in Latin script over Cyrillic: `sr-Latn:sr,sr:sr-Cyrl`


* There is also an `‑‑underscore` or `‑U` switch to publish locales with an underscore separator instead of a hyphen. This can be used in combination with aliases.

### Development Commands (debug mode only)

Expand All @@ -42,12 +73,12 @@ This command is to assist contributors to find any untranslated keys for their c

A stub JSON file will be created at `storage_path('app/nova-lang/missing/{locale}.json')`. You can copy those keys into the `resources/lang/{locale}.json` language file in your own fork of the repository, translate them and create a pull request.

Output missing translation keys for one or more languages:
* Output missing translation keys for one or more languages:
```bash
php artisan nova-lang:missing de,ru
```

Output missing translation keys for all languages:
* Output missing translation keys for all languages:
```bash
php artisan nova-lang:missing --all
```
Expand All @@ -58,7 +89,7 @@ This command is to assist maintainers to update the completeness of each languag

A `README.excerpt.md` and `contributors.json` file will be created at `storage_path('app/nova-lang')`. You can copy those files into your own fork of the repository and create a pull request.

Output list of languages, lines translated and contributors:
* Output list of languages, lines translated and contributors:
```bash
php artisan nova-lang:stats
```
Expand Down
111 changes: 102 additions & 9 deletions src/Commands/NovaLangPublish.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

class NovaLangPublish extends Command
{
/**
* Possible locale separators.
* @var string
*/
const SEPARATORS = '-‑_';

/**
* The name and signature of the console command.
*
Expand All @@ -17,6 +23,8 @@ class NovaLangPublish extends Command
protected $signature = 'nova-lang:publish
{locales? : Comma-separated list of languages}
{--all : Publish all languages}
{--alias= : Publish files using a different filename for certain locales, in the format "locale:alias,..."}
{--U|underscore : Use underscore instead of dash as locale separator }
{--force : Override existing files}';

/**
Expand Down Expand Up @@ -59,23 +67,33 @@ public function handle()
return;
}

$requestedLocales->each(function (string $locale) use ($availableLocales) {
$requestedLocales->each(function (string $alias, string $locale) use ($availableLocales) {

if ($locale == 'en' && $this->isForce()) {
if (!$this->confirm(sprintf('Are you sure you want to republish translations for [en] locale? This will overwrite the latest file from laravel/nova.'))) {
if ($alias == 'en' && $this->isForce()) {
if (!$this->confirm(sprintf('Are you sure you want to publish translations for [en] locale? This will overwrite the file from laravel/nova.'))) {
return;
}
}

if (! $availableLocales->contains($locale)) {
$this->error(sprintf('Unfortunately, translations for [%s] locale don\'t exist. Feel free to send a PR to add them and help other people :)', $locale));
$this->warn(sprintf('Unfortunately, translations for [%s] locale don\'t exist. Feel free to send a PR to add them and help other people.', $locale));

return;
}

$asAlias = '';

if ($this->option('underscore')) {
$alias = $this->fixSeparators($alias, '_');
}

if ($alias !== $locale) {
$asAlias = sprintf(' as [%s]', $alias);
}

$inputDirectory = $this->directoryFrom().'/'.$locale;

$outputDirectory = $this->directoryTo().'/'.$locale;
$outputDirectory = $this->directoryTo().'/'.$alias;

$inputFile = $inputDirectory.'.json';

Expand All @@ -84,7 +102,7 @@ public function handle()
if (($this->filesystem->exists($outputDirectory)
|| $this->filesystem->exists($outputFile))
&& ! $this->isForce()) {
$this->error(sprintf('Translations for [%s] locale already exist.', $locale));
$this->warn(sprintf('Translations for [%s] locale already exist%s. Use --force to overwrite.', $locale, $asAlias));

return;
}
Expand All @@ -95,17 +113,37 @@ public function handle()

$this->filesystem->copy($inputFile, $outputFile);

$this->info(sprintf('Translations for [%s] locale have been published successfully.', $locale));
$this->info(sprintf('Translations for [%s] locale have been published successfully%s.', $locale, $asAlias));
});
}

protected function getRequestedLocales(): Collection
{
if ($this->isAll()) {
return $this->getAvailableLocales();
$locales = $this->getAvailableLocales();
}
else {
$locales = $this->fixSeparators($this->argument('locales'));
$locales = collect(explode(',', $locales))->filter();
}

$aliases = $this->getLocaleAliases($locales->count() == 1 ? $locales->first() : false);

$locales = $locales->mapWithKeys(function (string $locale, string $alias) use (&$aliases) {
$alias = $aliases->pull($locale, $locale);

return [$locale => $alias];
});

if ($aliases->count()) {
$aliases = $aliases->map(function (string $locale, string $alias) {
return "$alias:$locale";
})->join(',');

$this->info(sprintf('Aliases [%s] were not used by the selected locales.', $aliases));
}

return collect(explode(',', $this->argument('locales')))->filter();
return $locales;
}

protected function getAvailableLocales(): Collection
Expand All @@ -123,6 +161,61 @@ protected function getAvailableLocales(): Collection
return $localesByDirectories->intersect($localesByFiles)->values();
}

protected function getLocaleAliases($single = false): Collection
{
$aliases = collect();

$input = $this->option('alias');

if ($input) {

$inputs = explode(',', $input);

if (strpos($input, ':') === false) {
if ($single && count($inputs) == 1) {
return collect([$single => $input]);
}

$this->error('If publishing more than one locale, the aliases must be in the format "locale:alias,...".');
exit;
}
elseif (substr_count($input, ':') < count($inputs)) {
if ($single) {
$this->error('If publishing only one locale with a simple alias, only one alias should be passed.');
}
else {
$this->error('If publishing more than one locale, the aliases must be in the format "locale:alias,...".');
}
exit;
}

foreach ($inputs as $input) {
@list($locale, $alias) = explode(':', $input);

if (empty($alias) || empty($locale)) {
$this->error(sprintf('Alias [%s] is not valid.', $input));
exit;
}

if ($aliases->has($locale)) {
$this->warn(sprintf('Alias for [%s] locale was declared more than once and will be overwritten by the last value.', $locale));
}


$locale = $this->fixSeparators($locale);

$aliases->put($locale, $alias);
}
}

return $aliases;
}

protected function fixSeparators(string $locale, string $separator = '-')
{
return preg_replace('/['.static::SEPARATORS.']+/', $separator, $locale);
}

protected function isForce(): bool
{
return $this->option('force');
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/NovaLangStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ public function handle()

$localeStat = $contributors->get($locale, [
'name' => class_exists('Locale') ? \Locale::getDisplayName($locale) : $locale,
'complete' => 0,
'complete' => null,
'contributors' => [],
]);

$complete = $sourceCount - count($missingKeys) - count($missingPhpKeys);

if ($complete > 0) {
if (!is_null($complete) && $complete > 0) {

if ($blameContributors = $blame->get($locale)) {
foreach ($blameContributors as $contributor => $lines) {
Expand Down