Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for localized strings #120

Merged
merged 12 commits into from
Dec 4, 2024
Merged
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