Skip to content

Commit

Permalink
Merge pull request #120 from dimtrovich/translations
Browse files Browse the repository at this point in the history
feat: Support for localized strings
  • Loading branch information
adhocore authored Dec 4, 2024
2 parents cd0152f + be819d4 commit 86be16e
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 54 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,29 @@ Whenever an exception is caught by `Application::handle()`, it will show a beaut

![Exception Preview](https://user-images.githubusercontent.com/2908547/44401057-8b350880-a577-11e8-8ca6-20508d593d98.png "Exception trace")

### Autocompletion
## I18n Support

**adhocore/cli** also supports internationalisation. This is particularly useful if you are not very comfortable with English or if you are creating a framework or CLI application that could be used by people from a variety of backgrounds.

By default, all the texts generated by our system are in English. But you can easily modify them by defining your translations as follows

```php
\Ahc\Application::addLocale('fr', [
'Only last argument can be variadic' => 'Seul le dernier argument peut être variadique',
], true);
```

You can also change the default English text to make the description more explicit if you wish.

```php
\Ahc\Application::addLocale('en', [
'Show help' => 'Shows helpful information about a command',
]);
```

vous pouvez trouver toutes les clés de traduction supportées par le paquet dans cette gist : https://gist.github.com/dimtrovich/1597c16d5c74334e68eef15a4e7ba3fd

## Autocompletion

Any console applications that are built on top of **adhocore/cli** can entertain autocomplete of commands and options in zsh shell with oh-my-zsh.

Expand Down
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
"autoload": {
"psr-4": {
"Ahc\\Cli\\": "src/"
}
},
"files": [
"src/functions.php"
]
},
"autoload-dev": {
"psr-4": {
Expand Down
36 changes: 31 additions & 5 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Ahc\Cli;

use Ahc\Cli\Exception\InvalidArgumentException;
use Ahc\Cli\Helper\InflectsString;
use Ahc\Cli\Helper\OutputHelper;
use Ahc\Cli\Input\Command;
use Ahc\Cli\IO\Interactor;
Expand All @@ -28,7 +29,6 @@
use function is_array;
use function is_int;
use function method_exists;
use function sprintf;

/**
* A cli application.
Expand All @@ -40,6 +40,23 @@
*/
class Application
{
/**
* Locale of CLI.
*/
public static $locale = 'en';

/**
* list of translations for each supported locale
*
* @var array<string, array>
*
* @example
* ```php
* ['locale' => ['key1' => 'value1', 'key2' => 'value2']]
* ```
*/
public static $locales = [];

/** @var Command[] */
protected array $commands = [];

Expand Down Expand Up @@ -130,6 +147,15 @@ public function logo(?string $logo = null)
return $this;
}

public static function addLocale(string $locale, array $texts, bool $default = false)
{
if ($default) {
self::$locale = $locale;
}

self::$locales[$locale] = $texts;
}

/**
* Add a command by its name desc alias etc and return command.
*/
Expand Down Expand Up @@ -161,7 +187,7 @@ public function add(Command $command, string $alias = '', bool $default = false)
$this->aliases[$alias] ??
null
) {
throw new InvalidArgumentException(sprintf('Command "%s" already added', $name));
throw new InvalidArgumentException(t('Command "%s" already added', [$name]));
}

if ($alias) {
Expand Down Expand Up @@ -190,7 +216,7 @@ public function add(Command $command, string $alias = '', bool $default = false)
public function defaultCommand(string $commandName): self
{
if (!isset($this->commands[$commandName])) {
throw new InvalidArgumentException(sprintf('Command "%s" does not exist', $commandName));
throw new InvalidArgumentException(t('Command "%s" does not exist', [$commandName]));
}

$this->default = $commandName;
Expand Down Expand Up @@ -386,8 +412,8 @@ public function showHelp(): mixed
public function showDefaultHelp(): mixed
{
$writer = $this->io()->writer();
$header = "{$this->name}, version {$this->version}";
$footer = 'Run `<command> --help` for specific help';
$header = "{$this->name}, " . t('version') . " {$this->version}";
$footer = t('Run `<command> --help` for specific help');

if ($this->logo) {
$writer->logo($this->logo, true);
Expand Down
19 changes: 11 additions & 8 deletions src/Helper/OutputHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Ahc\Cli\Output\Writer;
use Throwable;

use function Ahc\Cli\t;
use function array_map;
use function array_shift;
use function asort;
Expand Down Expand Up @@ -58,6 +59,8 @@
*/
class OutputHelper
{
use InflectsString;

protected Writer $writer;

/** @var int Max width of command name */
Expand All @@ -77,7 +80,7 @@ public function printTrace(Throwable $e): void

$this->writer->colors(
"{$eClass} <red>{$e->getMessage()}</end><eol/>" .
"(thrown in <yellow>{$e->getFile()}</end><white>:{$e->getLine()})</end>"
'(' . t('thrown in') . " <yellow>{$e->getFile()}</end><white>:{$e->getLine()})</end>"
);

// @codeCoverageIgnoreStart
Expand All @@ -87,7 +90,7 @@ public function printTrace(Throwable $e): void
}
// @codeCoverageIgnoreEnd

$traceStr = '<eol/><eol/><bold>Stack Trace:</end><eol/><eol/>';
$traceStr = '<eol/><eol/><bold>' . t('Stack Trace') . ':</end><eol/><eol/>';

foreach ($e->getTrace() as $i => $trace) {
$trace += ['class' => '', 'type' => '', 'function' => '', 'file' => '', 'line' => '', 'args' => []];
Expand All @@ -97,7 +100,7 @@ public function printTrace(Throwable $e): void
$traceStr .= " <comment>$i)</end> <red>$symbol</end><comment>($args)</end>";
if ('' !== $trace['file']) {
$file = realpath($trace['file']);
$traceStr .= "<eol/> <yellow>at $file</end><white>:{$trace['line']}</end><eol/>";
$traceStr .= "<eol/> <yellow>" . t('at') . " $file</end><white>:{$trace['line']}</end><eol/>";
}
}

Expand Down Expand Up @@ -185,7 +188,7 @@ protected function showHelp(string $for, array $items, string $header = '', stri
$this->writer->help_header($header, true);
}

$this->writer->eol()->help_category($for . ':', true);
$this->writer->eol()->help_category(t($for) . ':', true);

if (empty($items)) {
$this->writer->help_text(' (n/a)', true);
Expand Down Expand Up @@ -229,7 +232,7 @@ public function showUsage(string $usage): self
$usage = str_replace('$0', $_SERVER['argv'][0] ?? '[cmd]', $usage);

if (!str_contains($usage, ' ## ')) {
$this->writer->eol()->help_category('Usage Examples:', true)->colors($usage)->eol();
$this->writer->eol()->help_category(t('Usage Examples') . ':', true)->colors($usage)->eol();

return $this;
}
Expand All @@ -246,7 +249,7 @@ public function showUsage(string $usage): self
return str_pad('# ', $maxlen - array_shift($lines), ' ', STR_PAD_LEFT);
}, $usage);

$this->writer->eol()->help_category('Usage Examples:', true)->colors($usage)->eol();
$this->writer->eol()->help_category(t('Usage Examples') . ':', true)->colors($usage)->eol();

return $this;
}
Expand All @@ -261,11 +264,11 @@ public function showCommandNotFound(string $attempted, array $available): self
}
}

$this->writer->error("Command $attempted not found", true);
$this->writer->error(t('Command %s not found', [$attempted]), true);
if ($closest) {
asort($closest);
$closest = key($closest);
$this->writer->bgRed("Did you mean $closest?", true);
$this->writer->bgRed(t('Did you mean %s?', [$closest]), true);
}

return $this;
Expand Down
9 changes: 5 additions & 4 deletions src/Helper/Shell.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Ahc\Cli\Exception\RuntimeException;

use function Ahc\Cli\t;
use function fclose;
use function function_exists;
use function fwrite;
Expand Down Expand Up @@ -99,7 +100,7 @@ public function __construct(protected string $command, protected ?string $input
{
// @codeCoverageIgnoreStart
if (!function_exists('proc_open')) {
throw new RuntimeException('Required proc_open could not be found in your PHP setup.');
throw new RuntimeException(t('Required proc_open could not be found in your PHP setup.'));
}
// @codeCoverageIgnoreEnd

Expand Down Expand Up @@ -181,7 +182,7 @@ protected function checkTimeout(): void
if ($executionDuration > $this->processTimeout) {
$this->kill();

throw new RuntimeException('Timeout occurred, process terminated.');
throw new RuntimeException(t('Timeout occurred, process terminated.'));
}
// @codeCoverageIgnoreStart
}
Expand Down Expand Up @@ -216,7 +217,7 @@ public function setOptions(
public function execute(bool $async = false, ?array $stdin = null, ?array $stdout = null, ?array $stderr = null): self
{
if ($this->isRunning()) {
throw new RuntimeException('Process is already running.');
throw new RuntimeException(t('Process is already running.'));
}

$this->descriptors = $this->prepareDescriptors($stdin, $stdout, $stderr);
Expand All @@ -234,7 +235,7 @@ public function execute(bool $async = false, ?array $stdin = null, ?array $stdou

// @codeCoverageIgnoreStart
if (!is_resource($this->process)) {
throw new RuntimeException('Bad program could not be started.');
throw new RuntimeException(t('Bad program could not be started.'));
}
// @codeCoverageIgnoreEnd

Expand Down
5 changes: 3 additions & 2 deletions src/IO/Interactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Ahc\Cli\Output\Writer;
use Throwable;

use function Ahc\Cli\t;
use function array_keys;
use function array_map;
use function count;
Expand Down Expand Up @@ -312,7 +313,7 @@ public function choices(string $text, array $choices, $default = null, bool $cas
*/
public function prompt(string $text, $default = null, ?callable $fn = null, int $retry = 3): mixed
{
$error = 'Invalid value. Please try again!';
$error = t('Invalid value. Please try again!');
$hidden = func_get_args()[4] ?? false;
$readFn = ['read', 'readHidden'][(int) $hidden];

Expand Down Expand Up @@ -370,7 +371,7 @@ protected function listOptions(array $choices, $default = null, bool $multi = fa
$this->writer->eol()->choice(str_pad(" [$choice]", $maxLen + 6))->answer($desc);
}

$label = $multi ? 'Choices (comma separated)' : 'Choice';
$label = t($multi ? 'Choices (comma separated)' : 'Choice');

$this->writer->eol()->question($label);

Expand Down
20 changes: 9 additions & 11 deletions src/Input/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
use Ahc\Cli\Output\Writer;
use Closure;

use function Ahc\Cli\t;
use function array_filter;
use function array_keys;
use function end;
use function explode;
use function func_num_args;
use function sprintf;
use function str_contains;
use function strstr;

Expand Down Expand Up @@ -83,9 +83,9 @@ public function __construct(
*/
protected function defaults(): self
{
$this->option('-h, --help', 'Show help')->on([$this, 'showHelp']);
$this->option('-V, --version', 'Show version')->on([$this, 'showVersion']);
$this->option('-v, --verbosity', 'Verbosity level', null, 0)->on(
$this->option('-h, --help', t('Show help'))->on([$this, 'showHelp']);
$this->option('-V, --version', t('Show version'))->on([$this, 'showVersion']);
$this->option('-v, --verbosity', t('Verbosity level'), null, 0)->on(
fn () => $this->set('verbosity', ($this->verbosity ?? 0) + 1) && false
);

Expand Down Expand Up @@ -196,7 +196,7 @@ public function argument(string $raw, string $desc = '', $default = null): self
$argument = new Argument($raw, $desc, $default);

if ($this->_argVariadic) {
throw new InvalidParameterException('Only last argument can be variadic');
throw new InvalidParameterException(t('Only last argument can be variadic'));
}

if ($argument->variadic()) {
Expand Down Expand Up @@ -303,9 +303,7 @@ protected function handleUnknown(string $arg, ?string $value = null): mixed

// Has some value, error!
if ($values) {
throw new RuntimeException(
sprintf('Option "%s" not registered', $arg)
);
throw new RuntimeException(t('Option "%s" not registered', [$arg]));
}

// Has no value, show help!
Expand Down Expand Up @@ -358,13 +356,13 @@ public function showDefaultHelp(): mixed
$io->logo($logo, true);
}

$io->help_header("Command {$this->_name}, version {$this->_version}", true)->eol();
$io->help_header(t('Command') . " {$this->_name}, " . t('version') . " {$this->_version}", true)->eol();
$io->help_summary($this->_desc, true)->eol();
$io->help_text('Usage: ')->help_example("{$this->_name} [OPTIONS...] [ARGUMENTS...]", true);
$io->help_text(t('Usage') . ': ')->help_example("{$this->_name} " . t('[OPTIONS...] [ARGUMENTS...]'), true);

$helper
->showArgumentsHelp($this->allArguments())
->showOptionsHelp($this->allOptions(), '', 'Legend: <required> [optional] variadic...');
->showOptionsHelp($this->allOptions(), '', t('Legend: <required> [optional] variadic...'));

if ($this->_usage) {
$helper->showUsage($this->_usage);
Expand Down
4 changes: 2 additions & 2 deletions src/Input/Parameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

use Ahc\Cli\Helper\InflectsString;

use function Ahc\Cli\t;
use function json_encode;
use function ltrim;
use function strpos;
use function sprintf;

/**
* Cli Parameter.
Expand Down Expand Up @@ -84,7 +84,7 @@ public function desc(bool $withDefault = false): string
return $this->desc;
}

return ltrim(sprintf('%s [default: %s]', $this->desc, json_encode($this->default)));
return ltrim(t('%1$s [default: %2$s]', [$this->desc, json_encode($this->default)]));
}

/**
Expand Down
Loading

0 comments on commit 86be16e

Please sign in to comment.