Perl App::Cmd localization involves translating a command-line application’s command summaries, option descriptions, usage guidance, validation explanations and errors while retaining the exact command grammar scripts depend upon. This App::Cmd tutorial explains command plugins, opt_spec, usage_desc, abstract, description, validate_args and usage_error, with practical multilingual testing for Perl utilities. The goal is to make a long-lived CLI approachable without rewriting its public interface by accident.
Consider RecordVault, a fictional local records-catalog checker. Its maintainers already ship recordvault inspect catalogue.tsv –dry-run, and automation across several countries relies on that command. A team member needs a natural Japanese explanation of an invalid file and a French description of dry-run. Translating a code example’s inspect token into another word can break invocation, while translating a meaningful warning accurately can prevent a dangerous mistaken export.
The central rule is to preserve command plugin identity, option keys and real output contracts; translate abstract, description and human-facing guidance at controlled boundaries; and test both successful and invalid commands against the installed App::Cmd version. App::Cmd 0.340 is documented on MetaCPAN, including its interaction with Getopt::Long::Descriptive and Pod::Usage. Because a dependency’s native help and error rendering may not expose a universal locale switch, the safe design separates verified library behavior from application-owned localization.
Quick answer — how to localize Perl App::Cmd safely
- Keep command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status exact in runnable examples and program behavior.
- Translate abstract, description, usage_desc, opt_spec option explanations, usage_error recovery context and app-owned progress messages in a controlled language layer.
- Distinguish library-generated text from messages owned by the application.
- Pin and inspect the installed Perl dependencies before relying on a public example.
- Test valid commands, missing values, invalid options and unsafe side-effect claims.
- Capture real stdout, stderr and exit status; compare typed outputs instead of translated strings.
- Test Unicode filenames, narrow terminals, right-to-left explanations and copied commands.
- Record a supported-language fallback and ask native speakers to complete a real task.
Chapter 1 — What the command-line interface actually does
A command-line interface is a compact language. It combines a program name, selected action, options and data supplied by the reader. In Perl App::Cmd, App::Cmd discovers command plugin classes; App::Cmd::Command provides abstract, description, opt_spec, validate_args, usage_desc, usage_error and execute; Getopt::Long::Descriptive and Pod::Usage participate in help generation. This arrangement is valuable because the program can recognize the intended operation without interpreting an entire natural-language sentence. It is also why careless translation is risky: replacing a recognized command with a fluent but unimplemented synonym is equivalent to changing the program, not improving its documentation.
Begin with a task the reader already understands: RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings. The example task is recordvault inspect catalogue.tsv –dry-run. Before translating anything, identify the input file, the executable tokens, the expected result and possible side effects. A professional guide is useful when it answers what will change, what will not change, how to confirm success, and what happens if a step fails. Those four questions are more important than showing every optional switch on the first screen.
Chapter 2 — Separate executable identity from language
The simplest reliable distinction is between a machine token and a human description. command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status belong to the interface contract. abstract, description, usage_desc, opt_spec option explanations, usage_error recovery context and app-owned progress messages are candidates for localization, provided the chosen software architecture can display the translated text. A filename or number is usually data, which needs its own validation and formatting policy. The fact that all three may appear in one help line does not make them interchangeable.
Create a small inventory with columns for the visible string, where it originates, who owns it, whether users must type it and what tests protect it. For example, a translated phrase can sit beside –help, while –help itself remains literal. Reconfirm where the string is actually rendered: command help, validation error, progress output, a manual page, a generated completion script or a copied example. A change to a help paragraph should not need to rewire parsing.
Chapter 3 — A small version-aware technical example
The following Perl excerpt is a compact illustration of the code boundary. It is not an invitation to run unreviewed operations against real records. Put equivalent code in a disposable project, pin the library version and test with sample inputs. If your installed package differs from the cited documentation, inspect its public API first. Some applications wrap libraries with their own helper layers, so a team’s production command may accept different options from this educational example.
Read the example in two directions. First, follow the value typed by a user through the parser and into the program’s action. Second, find which strings describe that process to the reader. Translation belongs primarily in the second path, while validation and successful execution belong in the first. If the input path or option set changes when only the description language changes, that is evidence of a behavioral regression.
# In lib/RecordVault.pm:
package RecordVault;
use App::Cmd::Setup -app;
1;
# In lib/RecordVault/Command/inspect.pm:
package RecordVault::Command::inspect;
use RecordVault -command;
sub abstract { "Inspect a local catalog" }
sub description { "Check a tabular archive export without modifying it" }
sub opt_spec {
return ( [ "dry-run|n", "Report proposed changes only" ] );
}
sub validate_args {
my ($self, $opt, $args) = @_;
$self->usage_error("Expected one catalogue file") unless @$args == 1;
}
sub execute {
my ($self, $opt, $args) = @_;
print "Input: $args->[0]\n";
}
1;
Chapter 4 — Help must form a useful path, not a dictionary
A good help page begins with purpose, then a minimum safe invocation, then common arguments, then the next action after success or failure. It should not force a novice to decode a complete reference table before trying a single harmless example. Show a real path format, state whether it refers to a file or folder, identify default behavior, and explain what the command will write. Use short option descriptions for scanning, followed by detailed discussion for ambiguous or risky actions.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, start with a local fixture and a clear observation such as a row count, a checked schema, or a summarized warning. Present a command in code formatting and keep its spelling stable. The surrounding explanation can adapt to local grammar, but the command cannot change unless the program really supports another syntax. Think of translated help as a trustworthy user journey rather than a list of words that happen to be equivalent.
Chapter 5 — Choose a locale without changing the task
A multilingual application needs to determine the language before it constructs human-facing output. Depending on the application’s design, a documented flag, environment variable or configuration setting may be appropriate. Use predictable fallbacks and support an explicit list of locale tags. A user who requests Japanese help should not accidentally run a different operation, write to a different directory or grant broader permissions. Locale selection is a presentation decision until the application explicitly documents a different contract.
Avoid assuming that a low-level parser library supplies a ready-made translation catalog. The technical reference may document description strings without providing message extraction, fallback or plural handling. A practical architecture keeps a catalog keyed by stable identifiers and lets application code choose complete sentences. The key is never itself a command flag. Record which messages still come from Perl App::Cmd so readers and maintainers do not confuse a partial translation with total support.
Chapter 6 — Make errors actionable, accurate and safe
An error message should answer what failed, where the user can look and which next action is safe. Missing arguments, unknown switches, unsupported values, unreadable files and malformed contents are different failures. Some are rejected by the parser before the application starts; others occur later. Translating them into one generic ‘operation failed’ sentence removes critical information. Where messages are controlled by a dependency and lack a documented translation hook, a localized application explanation can be safer than brittle rewriting of raw dependency output.
Never print a successful-completion message after a command that failed. Compare exit status and side effects as well as displayed wording. When errors mention sensitive paths, identifiers or internal details, avoid exposing secrets in the guise of helpful localization. Show enough context for recovery without turning a CLI diagnostic into a data leak. The correct reader-facing wording often includes a valid minimal invocation and a pointer to a local log reference.
Chapter 7 — Treat Unicode and typography as engineering issues
Translation changes the shape of text. French descriptions may be longer, Japanese glyphs may occupy multiple terminal display columns, Arabic paragraphs may mix right-to-left text with left-to-right options, and filenames can contain combining marks or spaces. A string that looks fine in a browser may be unreadable or uncopyable in a shell. The program’s data representation, stdout encoding, terminal font and renderer are separate boundaries worth checking.
Keep executable tokens in code formatting, preserve ASCII punctuation where it has syntax meaning, and test copied commands as bytes. Check a narrow terminal and a normal terminal, stdout piped into a file and errors written to stderr. A correct translation should help a reader act, not merely look attractive in the translator’s editor. Confirm the supported environments rather than claiming universal Unicode behavior from the language runtime alone.
Chapter 8 — Check real outcomes, not just translated snapshots
A screenshot proves that text appeared. It does not prove that an operator selected the correct input, that the parser produced the intended value or that a command avoided an unpromised write. Build fixtures for a valid invocation, missing required input, malformed value, unknown option, Unicode path and a permissions failure. Compare parsed outputs, exit code and file effects across languages. The content of the sentence may change, but the operational result should stay consistent.
Native-language testing adds a different kind of evidence. Ask a reader to explain what the command will do before executing it, which part of the example is a placeholder, and what the next step is after a failure. A reviewer who can translate every word but selects the wrong operation has found a usability defect. Use that observation to fix help ordering, not just vocabulary.
Chapter 9 — Perl App::Cmd design decisions that change translation quality
1. The plugin name is executable structure
App::Cmd organizes actions into command plugin classes such as RecordVault::Command::inspect. By default, a command name may be derived from the package name. That gives translators a strong reason to keep command spellings in copyable invocations exact. A translated abstract is an explanation of the command, not a replacement for its registered name.
To make this decision useful for a real reader, first identify which part of recordvault inspect catalogue.tsv –dry-run is a typed token, which part is an input value, and which part is merely explanatory. Record the behavior before revising the wording. When a localized page makes a promise, turn that promise into a fixture: invoke a valid sample command, inspect the structured result and check that side effects match the guide.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
2. abstract and description have different work
abstract is a short summary shown when a user scans commands, while description can supply fuller prose in help. Do not force the same sentence into both slots. A good abstract identifies the action and object in a few words, and a good description clarifies inputs, effects and safety constraints. Translation quality should reflect those reading contexts.
This issue usually appears when source code, an English help screen and a translator’s draft are reviewed separately. Put them side by side. Ask the maintainer to show what Perl App::Cmd actually accepts, ask the language reviewer to describe the intended operation without consulting English, and let the test suite arbitrate differences. A good edit survives both kinds of review.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
3. opt_spec is not just prose
The opt_spec method returns option specifications for Getopt::Long::Descriptive. In an item such as dry-run|n, the name and alias describe parser grammar, while a separate string describes what the switch does. Keep the specification itself stable. Translating the explanatory string is different from changing the parsed option key that code later reads.
A small example is better than a sweeping claim. Build a disposable input, run it once successfully and then change only the display language. If the parsed values or selected files differ, the localization boundary has moved too far. If behavior matches but the reader cannot explain what happened, the prose is still not adequate. Both outcomes deserve independent fixes.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
4. usage_desc must be literal about syntax
usage_desc can change the displayed usage shape. The default includes placeholders for program, command and options. Translate framing carefully, but do not invent a positional value that the validator does not accept. If the command requires exactly one catalog path, the usage text must say so and a test must exercise the absent and extra-argument branches.
Keep a note of ownership and version. If the wording comes from library help generation, check the installed public API. If the wording comes from the application, change it with a catalog key and a semantic test. Never translate an internal identifier simply because it looks English, and never leave a user without an explanation because a technical token must remain stable.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
5. validate_args controls the contract
A command’s validate_args receives parsed options and remaining arguments before execution. It may call usage_error when a required condition is not met. The translated error should identify the rule violated: zero files, two files or an unsupported input format are distinct. A message that merely says ‘command invalid’ offers little recovery value.
To make this decision useful for a real reader, first identify which part of recordvault inspect catalogue.tsv –dry-run is a typed token, which part is an input value, and which part is merely explanatory. Record the behavior before revising the wording. When a localized page makes a promise, turn that promise into a fixture: invoke a valid sample command, inspect the structured result and check that side effects match the guide.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
6. usage_error carries recovery risk
usage_error is an intentional way to show human-friendly usage output after a validation problem. That makes it an important localization touchpoint, but changing the wording must not turn validation failure into success. Retain non-zero failure behavior and explain the next permitted command using the literal option spellings.
This issue usually appears when source code, an English help screen and a translator’s draft are reviewed separately. Put them side by side. Ask the maintainer to show what Perl App::Cmd actually accepts, ask the language reviewer to describe the intended operation without consulting English, and let the test suite arbitrate differences. A good edit survives both kinds of review.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
7. execute belongs to business logic
The execute method receives parsed options and positional arguments and performs the command. Place success wording after the operation, not before it. A catalog verification utility must distinguish a command that parsed successfully from a catalog that passed validation. Translators should not equate those states even if both appear near the end of an invocation.
A small example is better than a sweeping claim. Build a disposable input, run it once successfully and then change only the display language. If the parsed values or selected files differ, the localization boundary has moved too far. If behavior matches but the reader cannot explain what happened, the prose is still not adequate. Both outcomes deserve independent fixes.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
8. Global options may differ from command options
The App::Cmd application class may define global_opt_spec, while each command plugin defines opt_spec. A team that translates documentation without distinguishing those levels can instruct users to put options in the wrong place. Inspect actual global usage and command-specific usage and write runnable examples for both.
Keep a note of ownership and version. If the wording comes from library help generation, check the installed public API. If the wording comes from the application, change it with a catalog key and a semantic test. Never translate an internal identifier simply because it looks English, and never leave a user without an explanation because a technical token must remain stable.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
9. Aliases are supported, not invented
A command plugin may provide command_names with a canonical name and aliases. There is no reason to claim that translated aliases already exist. Introducing new aliases affects help, ambiguity handling, scripts and potentially discoverability; treat it as an engineering change with tests, rather than an automatic translation transformation.
To make this decision useful for a real reader, first identify which part of recordvault inspect catalogue.tsv –dry-run is a typed token, which part is an input value, and which part is merely explanatory. Record the behavior before revising the wording. When a localized page makes a promise, turn that promise into a fixture: invoke a valid sample command, inspect the structured result and check that side effects match the guide.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
10. POD and generated help can disagree
App::Cmd documentation notes that descriptions may be drawn from POD using Pod::Usage when a method is not overridden. Translating the method while leaving POD unchanged can produce mixed-language help, and translating POD alone may not affect an overridden description. Map each output string to its real source before editing.
This issue usually appears when source code, an English help screen and a translator’s draft are reviewed separately. Put them side by side. Ask the maintainer to show what Perl App::Cmd actually accepts, ask the language reviewer to describe the intended operation without consulting English, and let the test suite arbitrate differences. A good edit survives both kinds of review.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
11. Text encoding crosses several layers
Perl command-line input, source files, terminal streams and file system paths are separate encoding boundaries. A Japanese filename may be valid while output to a legacy terminal still looks broken. Do not promise that adding ‘use utf8’ fixes every stream; test input decoding and output encoding where the application actually runs.
A small example is better than a sweeping claim. Build a disposable input, run it once successfully and then change only the display language. If the parsed values or selected files differ, the localization boundary has moved too far. If behavior matches but the reader cannot explain what happened, the prose is still not adequate. Both outcomes deserve independent fixes.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
12. Version compatibility is a user concern
App::Cmd 0.340 was released in March 2026, but many maintained tools pin older distributions. Document the version that produced the public examples, and avoid presenting a later API behavior as an unconditional fact. A compatibility matrix should show what the release actually tested, not all operating systems imaginable.
Keep a note of ownership and version. If the wording comes from library help generation, check the installed public API. If the wording comes from the application, change it with a catalog key and a semantic test. Never translate an internal identifier simply because it looks English, and never leave a user without an explanation because a technical token must remain stable.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
13. Error messages should not leak internals
If a broken TSV input produces an internal stack trace, translating that trace is not a safe localization plan. Catch predictable errors, preserve actionable evidence in logs and show a concise message that names the input and corrective step. Keep security details and sensitive paths out of public output.
To make this decision useful for a real reader, first identify which part of recordvault inspect catalogue.tsv –dry-run is a typed token, which part is an input value, and which part is merely explanatory. Record the behavior before revising the wording. When a localized page makes a promise, turn that promise into a fixture: invoke a valid sample command, inspect the structured result and check that side effects match the guide.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
14. Legacy aliases deserve careful stewardship
A widely used Perl CLI may have aliases that are part of operations manuals. When a new language is added, do not remove a historic alias because it looks unnatural. First measure usage, document canonical spelling, and provide a deprecation policy if change is truly needed. Language rollout must not silently become interface retirement.
This issue usually appears when source code, an English help screen and a translator’s draft are reviewed separately. Put them side by side. Ask the maintainer to show what Perl App::Cmd actually accepts, ask the language reviewer to describe the intended operation without consulting English, and let the test suite arbitrate differences. A good edit survives both kinds of review.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the acceptance test is concrete: the operator must be able to follow recordvault inspect catalogue.tsv –dry-run, tell which output is a preview or final result, and recover from an invalid invocation. The reviewer should record the exact Perl runtime and dependency versions used. This turns the topic from an abstract translation preference into a verifiable instruction that can be maintained through future releases.
Chapter 10 — Eighteen realistic troubleshooting investigations
The cases below describe different ways a Perl App::Cmd translation can be polished but wrong. Each has a symptom, a repair and a verification step. They are examples for training and testing, not claims about defects in the library itself. Use fictitious data, preserve any real system’s access controls and rerun relevant checks when the underlying package is upgraded.
Investigation 1: Option alias translated
What the operator encounters: A reviewer rewrites dry-run|n to a translated long flag, so legacy scripts stop passing the expected option. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Keep dry-run|n as the parser specification and translate only its explanatory description. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Invoke both –dry-run and -n against the documented release. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 2: Wrong namespace in help
What the operator encounters: The translated guide shows recordvault –dry-run inspect even though dry-run belongs to inspect, not the global options. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Move the flag to the valid command position and explain scope in a one-line usage example. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Run global help and inspect-specific help separately. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 3: Missing positional file
What the operator encounters: The usage wording sounds optional, but validate_args requires exactly one filename. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Use ‘required catalog path’ in the reader’s language and show a concrete safe sample. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Run with zero, one and two positionals and compare failure behavior. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 4: POD fallback ignored
What the operator encounters: The command’s description method overrides POD, but the translator updates only the POD DESCRIPTION section. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Find the actual source of each help sentence, then update the method-backed prose. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Capture generated help from the packaged distribution. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 5: Mixed-language help
What the operator encounters: The command’s abstract is translated while option descriptions and usage headings stay in English. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Define a catalog coverage matrix for each visible string owner; leave machine tokens unchanged. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Inspect global and leaf help in the target locale and mark intentional English tokens. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 6: Unsafe file mention
What the operator encounters: An error prints the full sensitive network path of a restricted catalog and the localized guide encourages sharing it. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Avoid exposing privileged path details in user-visible logs while providing a sanitized diagnostic reference. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Test representative errors with fake sensitive paths. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 7: Unrecognized command alias
What the operator encounters: A localizer suggests a Spanish alias never returned by command_names. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Remove the unsupported alias or implement it deliberately as a versioned feature. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Ask the live binary for command listing and test the alias. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 8: Wrong error category
What the operator encounters: A malformed TSV produces a validation failure, but the message says the option syntax is wrong. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Keep argument syntax and input-content validation separate, with distinct corrections. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Feed malformed content to a valid invocation and assert the right error. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 9: Unexpected default action
What the operator encounters: A translated guide implies inspection is read-only, while execute generates a report by default. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Audit actual side effects and make the explanation match the implementation; a real dry-run requires code enforcement. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Hash a disposable output directory before and after execution. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 10: Unicode terminal mismatch
What the operator encounters: Japanese help works in tests but renders as mojibake in a remote terminal with legacy settings. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Check Perl stream encoding, locale, terminal settings and application output behavior instead of blaming translation. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Capture byte streams and compare human rendering on supported terminals. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 11: Ambiguous command abbreviation
What the operator encounters: An App::Cmd application may allow unambiguous command abbreviations; translation tells readers the abbreviation is always stable. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Prefer canonical full commands in public examples and avoid relying on abbreviations that future commands can make ambiguous. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Add a mock new command in a test branch and check ambiguity. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 12: Option value wrongly localized
What the operator encounters: A message translates a machine-enumerated status value, which the validator no longer recognizes. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Explain the status in natural language but retain the accepted literal value in code examples. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Run accepted, invalid and case-variant status fixtures. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 13: Non-zero exit lost
What the operator encounters: A wrapper catches usage_error and then exits with success, making shell automation believe bad input is valid. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Restore failing status for invalid invocations; translate the explanation without changing the outcome. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Check shell exit codes and missing output. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 14: Faulty dry-run promise
What the operator encounters: The translated short description says ‘no changes’ but execute modifies a cache even with the flag. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Narrow wording or repair the implementation after auditing cache and temporary-file effects. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Compare file timestamps and hashes in a clean fixture. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 15: Punctuation alters command
What the operator encounters: Smart typography transforms ASCII hyphens and quotes in code examples copied from a localized page. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Keep executable examples in literal code formatting and preserve exact shell characters. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Paste published commands into the supported shells. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 16: Help output overwritten
What the operator encounters: The app dynamically prints locale-specific help before calling App::Cmd, but still continues to execute the command. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Separate help-only execution from command dispatch and confirm it causes no writes. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Invoke help alongside a destructive-looking option in a sandbox. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 17: Deprecated command still documented
What the operator encounters: An earlier import command was removed, but its translated usage page remains linked from the new release. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Audit actual plugin discovery and versioned aliases, then label historical instructions honestly. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Run old and new command forms against the pinned release. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Investigation 18: Plural error oversimplified
What the operator encounters: A message that counts rejected records uses an English singular/plural pattern in a language with different number rules. This matters because help text and a working parser can disagree even when each looks coherent in isolation. A human may follow instructions correctly and still make the wrong call, which is a documentation defect as much as an implementation problem.
A safe correction is specific: Keep the numeric count as data and use appropriate full-message translation variants. Keep the change limited to the right layer. Do not alter command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status in response to a purely linguistic problem, and do not hide actual application defects beneath nicer wording.
Proof before release: Exercise 0, 1, 2, 5 and larger counts with target-language reviewers. Then repeat the attempt under the default language and at least one translated locale. Compare the result class, stderr and exit status, and inspect whether any output file, report or cache was changed. Mark a test as passing only when the technical result and the reader’s understanding agree.
Chapter 11 — A multilingual acceptance matrix
- English baseline: Use it to establish actual flags, commands, defaults, success behavior, and known error variants. Record this as a reference trace, not as the one correct grammatical form for every locale.
- French: Look for long descriptions, distinction between a command and a suggestion, correct treatment of filenames and quantities, and errors that say whether a file is absent or invalid.
- German: Check compound nouns, table widths, longer help lines and precise distinction between validation and execution. Confirm that examples still contain exact option names.
- Japanese: Check CJK terminal cell widths, natural task explanations, non-Latin filenames, copied quotes and an unambiguous explanation of values versus placeholders.
- Arabic: Check right-to-left prose beside left-to-right program tokens, numerals and paths. Verify that copied commands preserve byte order and the underlying command has not been visually rearranged.
- Hindi and Tamil: Test complex-script rendering, longer instructional sentences, font availability and clear presentation of syntax fragments. Do not use romanization as a substitute for genuine localized guidance without product justification.
- Spanish and Portuguese: Check regional variations, units, number formatting and whether translated verbs imply a read-only check or a write. Numeric argument syntax must remain what the program actually parses.
The matrix is a set of test questions, not evidence that these locales already ship with Perl App::Cmd. The application owner must say which languages are supported, how they are selected, who reviews them and which environments are in scope. It is better to support a few languages accurately than to advertise many through untranslated dependency errors and untested scripts.
Chapter 12 — Twelve reader-focused practical exercises
Exercise 1: The plugin name is executable structure
The purpose of this exercise is to make the plugin name is executable structure understandable to someone who has not studied the library source. First, have a learner predict the effect of a safe invocation before executing it; write down the predicted input, output and any possible side effects. Then change the display language, not the command, and have a second learner repeat the prediction without reading the first answer. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: App::Cmd organizes actions into command plugin classes such as RecordVault::Command::inspect. By default, a command name may be derived from the package name. That gives translators a strong reason to keep command spellings in copyable invocations exact. A translated abstract is an explanation of the command, not a replacement for its registered name. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 2: abstract and description have different work
The purpose of this exercise is to make abstract and description have different work understandable to someone who has not studied the library source. Use an intentionally invalid command to examine recovery guidance. Preserve the typed tokens in the report, identify the exact rejection stage, and ask the learner to choose the next permitted action rather than merely translate the displayed error. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: abstract is a short summary shown when a user scans commands, while description can supply fuller prose in help. Do not force the same sentence into both slots. A good abstract identifies the action and object in a few words, and a good description clarifies inputs, effects and safety constraints. Translation quality should reflect those reading contexts. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 3: opt_spec is not just prose
The purpose of this exercise is to make opt_spec is not just prose understandable to someone who has not studied the library source. Create two local fixtures: one valid and one that violates the condition under discussion. Test each in the base language and a translated language, comparing the parser result and real files, not just the text on screen. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: The opt_spec method returns option specifications for Getopt::Long::Descriptive. In an item such as dry-run|n, the name and alias describe parser grammar, while a separate string describes what the switch does. Keep the specification itself stable. Translating the explanatory string is different from changing the parsed option key that code later reads. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 4: usage_desc must be literal about syntax
The purpose of this exercise is to make usage_desc must be literal about syntax understandable to someone who has not studied the library source. Review a narrow-terminal rendering and a copied command. Ask the learner to identify which components are executable grammar, which are placeholders and which describe behavior. Then paste the command into a disposable environment and verify the claim. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: usage_desc can change the displayed usage shape. The default includes placeholders for program, command and options. Translate framing carefully, but do not invent a positional value that the validator does not accept. If the command requires exactly one catalog path, the usage text must say so and a test must exercise the absent and extra-argument branches. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 5: validate_args controls the contract
The purpose of this exercise is to make validate_args controls the contract understandable to someone who has not studied the library source. First, have a learner predict the effect of a safe invocation before executing it; write down the predicted input, output and any possible side effects. Then change the display language, not the command, and have a second learner repeat the prediction without reading the first answer. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: A command’s validate_args receives parsed options and remaining arguments before execution. It may call usage_error when a required condition is not met. The translated error should identify the rule violated: zero files, two files or an unsupported input format are distinct. A message that merely says ‘command invalid’ offers little recovery value. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 6: usage_error carries recovery risk
The purpose of this exercise is to make usage_error carries recovery risk understandable to someone who has not studied the library source. Use an intentionally invalid command to examine recovery guidance. Preserve the typed tokens in the report, identify the exact rejection stage, and ask the learner to choose the next permitted action rather than merely translate the displayed error. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: usage_error is an intentional way to show human-friendly usage output after a validation problem. That makes it an important localization touchpoint, but changing the wording must not turn validation failure into success. Retain non-zero failure behavior and explain the next permitted command using the literal option spellings. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 7: execute belongs to business logic
The purpose of this exercise is to make execute belongs to business logic understandable to someone who has not studied the library source. Create two local fixtures: one valid and one that violates the condition under discussion. Test each in the base language and a translated language, comparing the parser result and real files, not just the text on screen. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: The execute method receives parsed options and positional arguments and performs the command. Place success wording after the operation, not before it. A catalog verification utility must distinguish a command that parsed successfully from a catalog that passed validation. Translators should not equate those states even if both appear near the end of an invocation. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 8: Global options may differ from command options
The purpose of this exercise is to make global options may differ from command options understandable to someone who has not studied the library source. Review a narrow-terminal rendering and a copied command. Ask the learner to identify which components are executable grammar, which are placeholders and which describe behavior. Then paste the command into a disposable environment and verify the claim. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: The App::Cmd application class may define global_opt_spec, while each command plugin defines opt_spec. A team that translates documentation without distinguishing those levels can instruct users to put options in the wrong place. Inspect actual global usage and command-specific usage and write runnable examples for both. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 9: Aliases are supported, not invented
The purpose of this exercise is to make aliases are supported, not invented understandable to someone who has not studied the library source. First, have a learner predict the effect of a safe invocation before executing it; write down the predicted input, output and any possible side effects. Then change the display language, not the command, and have a second learner repeat the prediction without reading the first answer. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: A command plugin may provide command_names with a canonical name and aliases. There is no reason to claim that translated aliases already exist. Introducing new aliases affects help, ambiguity handling, scripts and potentially discoverability; treat it as an engineering change with tests, rather than an automatic translation transformation. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 10: POD and generated help can disagree
The purpose of this exercise is to make pod and generated help can disagree understandable to someone who has not studied the library source. Use an intentionally invalid command to examine recovery guidance. Preserve the typed tokens in the report, identify the exact rejection stage, and ask the learner to choose the next permitted action rather than merely translate the displayed error. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: App::Cmd documentation notes that descriptions may be drawn from POD using Pod::Usage when a method is not overridden. Translating the method while leaving POD unchanged can produce mixed-language help, and translating POD alone may not affect an overridden description. Map each output string to its real source before editing. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 11: Text encoding crosses several layers
The purpose of this exercise is to make text encoding crosses several layers understandable to someone who has not studied the library source. Create two local fixtures: one valid and one that violates the condition under discussion. Test each in the base language and a translated language, comparing the parser result and real files, not just the text on screen. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: Perl command-line input, source files, terminal streams and file system paths are separate encoding boundaries. A Japanese filename may be valid while output to a legacy terminal still looks broken. Do not promise that adding ‘use utf8’ fixes every stream; test input decoding and output encoding where the application actually runs. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Exercise 12: Version compatibility is a user concern
The purpose of this exercise is to make version compatibility is a user concern understandable to someone who has not studied the library source. Review a narrow-terminal rendering and a copied command. Ask the learner to identify which components are executable grammar, which are placeholders and which describe behavior. Then paste the command into a disposable environment and verify the claim. In RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, the worked example is recordvault inspect catalogue.tsv –dry-run. Use sample inputs only, and keep a record of the version, locale, expected result, actual result and the exact sentence that caused confusion.
The decision at stake is this: App::Cmd 0.340 was released in March 2026, but many maintained tools pin older distributions. Document the version that produced the public examples, and avoid presenting a later API behavior as an unconditional fact. A compatibility matrix should show what the release actually tested, not all operating systems imaginable. A reviewer should be able to explain why a misleading translation changes the operator’s decision, not merely say that two sentences sound different. If testing reveals an implementation bug, fix that separately and rerun the language review. A completed exercise leaves a small reproducible proof that the documentation matches the product.
Chapter 13 — Frequently asked questions
How is App::Cmd different from Getopt::Long?
App::Cmd organizes a multi-command application and delegates option description machinery to Getopt::Long::Descriptive. A translation guide should therefore separate command discovery, command-level help and option parsing behavior.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
Can I translate command package names?
Not safely as an ordinary copy edit. Package and registered command identity are executable concerns. Translate the user-facing abstract and description, and only add aliases after deliberate implementation and tests.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
Where does description text originate?
It can come from an overridden command method or, according to the documented fallback, POD. Inspect the actual command class to determine which source produced the visible output.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
What should a usage error say?
Name the rejected input condition, provide a valid example and explain the next safe action, without claiming that the command completed successfully.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
Do translated aliases automatically work?
No. A translated phrase is not a registered alias. Only a name that the installed application has explicitly configured and tested should appear as a copyable command.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
How should I treat legacy Perl versions?
Check the distribution and application version requirements, and test only the operating systems and Perl runtimes you genuinely support. Do not infer compatibility from an unrelated example.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
Why do non-English filenames need tests?
Command-line data may cross shell, Perl, filesystem and terminal encoding boundaries. A well-translated error cannot help when the input path is decoded incorrectly.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
What fails the publication gate?
An unrecognized published command, wrong argument count, false no-write claim, misleading successful exit, broken non-Latin example or missing recovery instructions.
For RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, settle this with a real command and a controlled fixture rather than intuition. A helpful answer names the stable part of command plugin package names and command aliases, option specifications, parsed option keys, positional inputs, validation behavior and exit status, identifies the reader-facing explanation, and shows how to verify the outcome without production data. Where support depends on package version, test the installed release rather than assuming universal behavior.
Chapter 14 — A four-week implementation and review plan
Week 1: inventory the interface
Capture root help, nested help, sample invocations and errors from the released application. Map every visible message to its owner, list supported commands, record exact dependency and runtime versions, and classify input values separately from strings meant for translation. Choose two real reader tasks. A good result for this week is a short baseline document with no uncertainty about what the program actually accepts.
Applied to Perl App::Cmd, this week’s exit condition must be checked in both code and language. Use recordvault inspect catalogue.tsv –dry-run with a disposable fixture, retain the original command spellings and compare the intended operation with the observed result. Do not advance merely because the text sounds polished; advance when a learner can predict and verify what the tool will do.
Week 2: draft and integrate language support
Choose supported locales and a fallback policy. Translate complete tasks, warnings and recovery instructions instead of isolated words, preserving fixed tokens. Connect application-owned strings to catalog keys or another version-appropriate system, and leave library-owned text identified where override support is unavailable. Ask developers to review every promise about writes, permissions, defaults, file formats and errors.
Applied to Perl App::Cmd, this week’s exit condition must be checked in both code and language. Use recordvault inspect catalogue.tsv –dry-run with a disposable fixture, retain the original command spellings and compare the intended operation with the observed result. Do not advance merely because the text sounds polished; advance when a learner can predict and verify what the tool will do.
Week 3: behavior and comprehension testing
Run successful and invalid input fixtures under each chosen locale. Compare parsed values, stdout/stderr channels, exit statuses and output effects; also test non-ASCII paths, terminal width and copied code snippets. Ask native-language reviewers to complete the two original tasks without English guidance and record where decisions fail. Correct both functional and wording faults; re-run the baseline tests.
Applied to Perl App::Cmd, this week’s exit condition must be checked in both code and language. Use recordvault inspect catalogue.tsv –dry-run with a disposable fixture, retain the original command spellings and compare the intended operation with the observed result. Do not advance merely because the text sounds polished; advance when a learner can predict and verify what the tool will do.
Week 4: publish, monitor and maintain
Finalize a short quick-start, supported-version note, option reference, troubleshooting guide and fallback disclosure. Publish only tested examples, with a release checklist signed by engineering and language reviewers. Keep the command contract versioned, record known limitations and add regression fixtures for defects found during real support requests. A translation is complete when a reader can finish the task safely, not when the catalog has no empty cells.
Applied to Perl App::Cmd, this week’s exit condition must be checked in both code and language. Use recordvault inspect catalogue.tsv –dry-run with a disposable fixture, retain the original command spellings and compare the intended operation with the observed result. Do not advance merely because the text sounds polished; advance when a learner can predict and verify what the tool will do.
Chapter 15 — Professional standard and next step
The best multilingual command-line documentation is boring in the most useful sense: the examples execute as written, the parser behaves consistently, the outputs say what happened and the reader knows how to repair a failure. With Perl App::Cmd, translation quality depends on respecting how the framework defines commands and values while giving humans accurate descriptions of those same structures.
Start from one small task in RecordVault, an illustrative records-catalog validator that checks local tabular exports and produces optional human-readable findings, verify it in the default language, translate its complete explanation and test the same action again. Expand to errors, more commands and additional locales only when earlier behavior stays stable. That sequence makes multilingual support maintainable for new contributors, reviewers and users who cannot ask the original developer what a sentence was supposed to mean.