diff --git a/README.md b/README.md index 3c2fcf4..b6ac02d 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 ``` @@ -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 ``` diff --git a/src/Commands/NovaLangPublish.php b/src/Commands/NovaLangPublish.php index 0f18ae3..e98f3a5 100644 --- a/src/Commands/NovaLangPublish.php +++ b/src/Commands/NovaLangPublish.php @@ -9,6 +9,12 @@ class NovaLangPublish extends Command { + /** + * Possible locale separators. + * @var string + */ + const SEPARATORS = '-‑_'; + /** * The name and signature of the console command. * @@ -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}'; /** @@ -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'; @@ -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; } @@ -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 @@ -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'); diff --git a/src/Commands/NovaLangStats.php b/src/Commands/NovaLangStats.php index ae063be..b05cb1a 100644 --- a/src/Commands/NovaLangStats.php +++ b/src/Commands/NovaLangStats.php @@ -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) {