Skip to content

Commit 2f187bd

Browse files
committed
refactor: using plain text instead of keys for translations
1 parent b8e089f commit 2f187bd

11 files changed

Lines changed: 74 additions & 47 deletions

File tree

src/Application.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Ahc\Cli;
1313

1414
use Ahc\Cli\Exception\InvalidArgumentException;
15+
use Ahc\Cli\Helper\InflectsString;
1516
use Ahc\Cli\Helper\OutputHelper;
1617
use Ahc\Cli\Input\Command;
1718
use Ahc\Cli\IO\Interactor;
@@ -28,7 +29,6 @@
2829
use function is_array;
2930
use function is_int;
3031
use function method_exists;
31-
use function sprintf;
3232

3333
/**
3434
* A cli application.
@@ -40,6 +40,25 @@
4040
*/
4141
class Application
4242
{
43+
use InflectsString;
44+
45+
/**
46+
* Locale of CLI.
47+
*/
48+
public static $locale = 'en';
49+
50+
/**
51+
* list of translations for each supported locale
52+
*
53+
* @var array<string, array>
54+
*
55+
* @example
56+
* ```php
57+
* ['locale' => ['key1' => 'value1', 'key2' => 'value2']]
58+
* ```
59+
*/
60+
public static $locales = [];
61+
4362
/** @var Command[] */
4463
protected array $commands = [];
4564

@@ -130,6 +149,15 @@ public function logo(?string $logo = null)
130149
return $this;
131150
}
132151

152+
public static function addLocale(string $locale, array $texts, bool $default = false)
153+
{
154+
if ($default) {
155+
self::$locale = $locale;
156+
}
157+
158+
self::$locales[$locale] = $texts;
159+
}
160+
133161
/**
134162
* Add a command by its name desc alias etc and return command.
135163
*/
@@ -161,7 +189,7 @@ public function add(Command $command, string $alias = '', bool $default = false)
161189
$this->aliases[$alias] ??
162190
null
163191
) {
164-
throw new InvalidArgumentException(sprintf('Command "%s" already added', $name));
192+
throw new InvalidArgumentException($this->translate('Command "%s" already added', [$name]));
165193
}
166194

167195
if ($alias) {
@@ -190,7 +218,7 @@ public function add(Command $command, string $alias = '', bool $default = false)
190218
public function defaultCommand(string $commandName): self
191219
{
192220
if (!isset($this->commands[$commandName])) {
193-
throw new InvalidArgumentException(sprintf('Command "%s" does not exist', $commandName));
221+
throw new InvalidArgumentException($this->translate('Command "%s" does not exist', [$commandName]));
194222
}
195223

196224
$this->default = $commandName;
@@ -386,8 +414,8 @@ public function showHelp(): mixed
386414
public function showDefaultHelp(): mixed
387415
{
388416
$writer = $this->io()->writer();
389-
$header = "{$this->name}, version {$this->version}";
390-
$footer = 'Run `<command> --help` for specific help';
417+
$header = "{$this->name}, {$this->translate('version')} {$this->version}";
418+
$footer = $this->translate('Run `<command> --help` for specific help');
391419

392420
if ($this->logo) {
393421
$writer->logo($this->logo, true);

src/Helper/InflectsString.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace Ahc\Cli\Helper;
1313

14-
use Ahc\Cli\Translations\Translator;
14+
use Ahc\Cli\Application;
1515

1616
use function lcfirst;
1717
use function mb_strwidth;
@@ -32,8 +32,6 @@
3232
*/
3333
trait InflectsString
3434
{
35-
private static ?Translator $translator = null;
36-
3735
/**
3836
* Convert a string to camel case.
3937
*/
@@ -83,12 +81,11 @@ public function substr(string $string, int $start, ?int $length = null): string
8381
/**
8482
* Translates a message using the translator.
8583
*/
86-
public static function translate(string $key, array $args = []): string
84+
public static function translate(string $text, array $args = []): string
8785
{
88-
if (self::$translator === null) {
89-
self::$translator = new Translator();
90-
}
86+
$translations = Application::$locales[Application::$locale] ?? [];
87+
$text = $translations[$text] ?? $text;
9188

92-
return self::$translator->getMessage($key, $args);
89+
return sprintf($text, ...$args);
9390
}
9491
}

src/Helper/OutputHelper.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public function printTrace(Throwable $e): void
7979

8080
$this->writer->colors(
8181
"{$eClass} <red>{$e->getMessage()}</end><eol/>" .
82-
"({$this->translate('thrownIn')} <yellow>{$e->getFile()}</end><white>:{$e->getLine()})</end>"
82+
"({$this->translate('thrown in')} <yellow>{$e->getFile()}</end><white>:{$e->getLine()})</end>"
8383
);
8484

8585
// @codeCoverageIgnoreStart
@@ -89,7 +89,7 @@ public function printTrace(Throwable $e): void
8989
}
9090
// @codeCoverageIgnoreEnd
9191

92-
$traceStr = "<eol/><eol/><bold>{$this->translate('stackTrace')}:</end><eol/><eol/>";
92+
$traceStr = "<eol/><eol/><bold>{$this->translate('Stack Trace')}:</end><eol/><eol/>";
9393

9494
foreach ($e->getTrace() as $i => $trace) {
9595
$trace += ['class' => '', 'type' => '', 'function' => '', 'file' => '', 'line' => '', 'args' => []];
@@ -99,7 +99,7 @@ public function printTrace(Throwable $e): void
9999
$traceStr .= " <comment>$i)</end> <red>$symbol</end><comment>($args)</end>";
100100
if ('' !== $trace['file']) {
101101
$file = realpath($trace['file']);
102-
$traceStr .= "<eol/> <yellow>{$this->translate('thrownAt')} $file</end><white>:{$trace['line']}</end><eol/>";
102+
$traceStr .= "<eol/> <yellow>{$this->translate('at')} $file</end><white>:{$trace['line']}</end><eol/>";
103103
}
104104
}
105105

@@ -187,7 +187,7 @@ protected function showHelp(string $for, array $items, string $header = '', stri
187187
$this->writer->help_header($header, true);
188188
}
189189

190-
$this->writer->eol()->help_category($this->translate(strtolower($for)) . ':', true);
190+
$this->writer->eol()->help_category($this->translate($for) . ':', true);
191191

192192
if (empty($items)) {
193193
$this->writer->help_text(' (n/a)', true);
@@ -231,7 +231,7 @@ public function showUsage(string $usage): self
231231
$usage = str_replace('$0', $_SERVER['argv'][0] ?? '[cmd]', $usage);
232232

233233
if (!str_contains($usage, ' ## ')) {
234-
$this->writer->eol()->help_category($this->translate('usageExamples') . ':', true)->colors($usage)->eol();
234+
$this->writer->eol()->help_category($this->translate('Usage Examples') . ':', true)->colors($usage)->eol();
235235

236236
return $this;
237237
}
@@ -248,7 +248,7 @@ public function showUsage(string $usage): self
248248
return str_pad('# ', $maxlen - array_shift($lines), ' ', STR_PAD_LEFT);
249249
}, $usage);
250250

251-
$this->writer->eol()->help_category($this->translate('usageExamples') . ':', true)->colors($usage)->eol();
251+
$this->writer->eol()->help_category($this->translate('Usage Examples') . ':', true)->colors($usage)->eol();
252252

253253
return $this;
254254
}
@@ -263,11 +263,11 @@ public function showCommandNotFound(string $attempted, array $available): self
263263
}
264264
}
265265

266-
$this->writer->error($this->translate('commandNotFound', [$attempted]), true);
266+
$this->writer->error($this->translate('Command %s not found', [$attempted]), true);
267267
if ($closest) {
268268
asort($closest);
269269
$closest = key($closest);
270-
$this->writer->bgRed($this->translate('commandSuggestion', [$closest]), true);
270+
$this->writer->bgRed($this->translate('Did you mean %s?', [$closest]), true);
271271
}
272272

273273
return $this;

src/Helper/Shell.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function __construct(protected string $command, protected ?string $input
101101
{
102102
// @codeCoverageIgnoreStart
103103
if (!function_exists('proc_open')) {
104-
throw new RuntimeException($this->translate('procOpenMissing'));
104+
throw new RuntimeException($this->translate('Required proc_open could not be found in your PHP setup.'));
105105
}
106106
// @codeCoverageIgnoreEnd
107107

@@ -183,7 +183,7 @@ protected function checkTimeout(): void
183183
if ($executionDuration > $this->processTimeout) {
184184
$this->kill();
185185

186-
throw new RuntimeException($this->translate('timeoutOccured'));
186+
throw new RuntimeException($this->translate('Timeout occurred, process terminated.'));
187187
}
188188
// @codeCoverageIgnoreStart
189189
}
@@ -218,7 +218,7 @@ public function setOptions(
218218
public function execute(bool $async = false, ?array $stdin = null, ?array $stdout = null, ?array $stderr = null): self
219219
{
220220
if ($this->isRunning()) {
221-
throw new RuntimeException($this->translate('processAlreadyRun'));
221+
throw new RuntimeException($this->translate('Process is already running.'));
222222
}
223223

224224
$this->descriptors = $this->prepareDescriptors($stdin, $stdout, $stderr);
@@ -236,7 +236,7 @@ public function execute(bool $async = false, ?array $stdin = null, ?array $stdou
236236

237237
// @codeCoverageIgnoreStart
238238
if (!is_resource($this->process)) {
239-
throw new RuntimeException($this->translate('badProgram'));
239+
throw new RuntimeException($this->translate('Bad program could not be started.'));
240240
}
241241
// @codeCoverageIgnoreEnd
242242

src/IO/Interactor.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ public function choices(string $text, array $choices, $default = null, bool $cas
315315
*/
316316
public function prompt(string $text, $default = null, ?callable $fn = null, int $retry = 3): mixed
317317
{
318-
$error = $this->translate('promptInvalidValue');
318+
$error = $this->translate('Invalid value. Please try again!');
319319
$hidden = func_get_args()[4] ?? false;
320320
$readFn = ['read', 'readHidden'][(int) $hidden];
321321

@@ -373,7 +373,7 @@ protected function listOptions(array $choices, $default = null, bool $multi = fa
373373
$this->writer->eol()->choice(str_pad(" [$choice]", $maxLen + 6))->answer($desc);
374374
}
375375

376-
$label = $this->translate($multi ? 'choices' : 'choice');
376+
$label = $this->translate($multi ? 'Choices (comma separated)' : 'Choice');
377377

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

src/Input/Command.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ public function __construct(
8383
*/
8484
protected function defaults(): self
8585
{
86-
$this->option('-h, --help', $this->translate('showHelp'))->on([$this, 'showHelp']);
87-
$this->option('-V, --version', $this->translate('showVersion'))->on([$this, 'showVersion']);
88-
$this->option('-v, --verbosity', $this->translate('verbosityLevel'), null, 0)->on(
86+
$this->option('-h, --help', $this->translate('Show help'))->on([$this, 'showHelp']);
87+
$this->option('-V, --version', $this->translate('Show version'))->on([$this, 'showVersion']);
88+
$this->option('-v, --verbosity', $this->translate('Verbosity level'), null, 0)->on(
8989
fn () => $this->set('verbosity', ($this->verbosity ?? 0) + 1) && false
9090
);
9191

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

198198
if ($this->_argVariadic) {
199-
throw new InvalidParameterException($this->translate('argumentVariadic'));
199+
throw new InvalidParameterException($this->translate('Only last argument can be variadic'));
200200
}
201201

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

304304
// Has some value, error!
305305
if ($values) {
306-
throw new RuntimeException($this->translate('optionNotRegistered', [$arg]));
306+
throw new RuntimeException($this->translate('Option "%s" not registered', [$arg]));
307307
}
308308

309309
// Has no value, show help!
@@ -356,13 +356,13 @@ public function showDefaultHelp(): mixed
356356
$io->logo($logo, true);
357357
}
358358

359-
$io->help_header("{$this->translate('command')} {$this->_name}, {$this->translate('version')} {$this->_version}", true)->eol();
359+
$io->help_header("{$this->translate('Command')} {$this->_name}, {$this->translate('version')} {$this->_version}", true)->eol();
360360
$io->help_summary($this->_desc, true)->eol();
361-
$io->help_text("{$this->translate('usage')}: ")->help_example("{$this->_name} {$this->translate('helpExample')}", true);
361+
$io->help_text("{$this->translate('Usage')}: ")->help_example("{$this->_name} {$this->translate('[OPTIONS...] [ARGUMENTS...]')}", true);
362362

363363
$helper
364364
->showArgumentsHelp($this->allArguments())
365-
->showOptionsHelp($this->allOptions(), '', $this->translate('optionHelp'));
365+
->showOptionsHelp($this->allOptions(), '', $this->translate('Legend: <required> [optional] variadic...'));
366366

367367
if ($this->_usage) {
368368
$helper->showUsage($this->_usage);

src/Input/Parameter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function desc(bool $withDefault = false): string
8383
return $this->desc;
8484
}
8585

86-
return ltrim($this->translate('descWithDefault', [$this->desc, json_encode($this->default)]));
86+
return ltrim($this->translate('%s [default: %s]', [$this->desc, json_encode($this->default)]));
8787
}
8888

8989
/**

src/Input/Parser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ protected function validate(): void
225225
}
226226

227227
throw new RuntimeException(
228-
sprintf('%s "%s" is required', $label, $name)
228+
$this->translate('%s "%s" is required', [$label, $name])
229229
);
230230
}
231231
}
@@ -268,7 +268,7 @@ protected function ifAlreadyRegistered(Parameter $param): void
268268
{
269269
if ($this->registered($param->attributeName())) {
270270
throw new InvalidParameterException($this->translate(
271-
'parameterAlreadyRegistered',
271+
'The parameter "%s" is already registered',
272272
[$param instanceof Option ? $param->long() : $param->name()]
273273
));
274274
}

src/Output/Color.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,12 @@ public static function style(string $name, array $style): void
194194
$style = array_intersect_key($style, $allow);
195195

196196
if (empty($style)) {
197-
throw new InvalidArgumentException(self::translate('usingInvalidStyle'));
197+
throw new InvalidArgumentException(self::translate('Trying to set empty or invalid style'));
198198
}
199199

200200
$invisible = (isset($style['bg']) && isset($style['fg']) && $style['bg'] === $style['fg']);
201201
if ($invisible && method_exists(static::class, $name)) {
202-
throw new InvalidArgumentException(self::translate('styleInvisible'));
202+
throw new InvalidArgumentException(self::translate('Built-in styles cannot be invisible'));
203203
}
204204

205205
static::$styles[$name] = $style;
@@ -216,7 +216,7 @@ public static function style(string $name, array $style): void
216216
public function __call(string $name, array $arguments): string
217217
{
218218
if (!isset($arguments[0])) {
219-
throw new InvalidArgumentException($this->translate('textRequired'));
219+
throw new InvalidArgumentException($this->translate('Text required'));
220220
}
221221

222222
[$name, $text, $style] = $this->parseCall($name, $arguments);
@@ -231,7 +231,7 @@ public function __call(string $name, array $arguments): string
231231
}
232232

233233
if (!method_exists($this, $name)) {
234-
throw new InvalidArgumentException($this->translate('undefinedStyle', [$name]));
234+
throw new InvalidArgumentException($this->translate('Style "%s" not defined', [$name]));
235235
}
236236

237237
return $this->{$name}($text, $style);

src/Output/ProgressBar.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public function option(string|array $key, ?string $value = null): self
136136
{
137137
if (is_string($key)) {
138138
if (empty($value)) {
139-
throw new UnexpectedValueException($this->translate('configOptionMissing'));
139+
throw new UnexpectedValueException($this->translate('Configuration option value is required'));
140140
}
141141

142142
$key = [$key => $value];
@@ -166,11 +166,13 @@ public function current(int $current, string $label = '')
166166
{
167167
if ($this->total == 0) {
168168
// Avoid dividing by 0
169-
throw new UnexpectedValueException($this->translate('progressbarTotalMin'));
169+
throw new UnexpectedValueException($this->translate('The progress total must be greater than zero.'));
170170
}
171-
171+
172172
if ($current > $this->total) {
173-
throw new UnexpectedValueException($this->translate('progressbarCurrentMax', [$current, $this->total]));
173+
throw new UnexpectedValueException(
174+
$this->translate('The current (%d) is greater than the total (%d).', [$current, $this->total])
175+
);
174176
}
175177

176178
$this->drawProgressBar($current, $label);

0 commit comments

Comments
 (0)