ReferenceCLIapigeniei18n
i18n
Extracts texts for localization from OpenAPI, Swagger 2.0 and AsyncAPI specifications and
Protobuf and GraphQL files, checks and updates their translations, and applies them. All
subcommands work on all five: i18n extract writes the texts as a key-value file, i18n check
tells whether a translation file is complete, i18n sync updates one after the document changed,
and i18n apply puts the translated values back.
Usage
apigenie i18n extract [OPTIONS] FILE
apigenie i18n check --translations TRANSLATIONS [OPTIONS] FILE
apigenie i18n sync --previous PREVIOUS --translations TRANSLATIONS [OPTIONS] FILE
apigenie i18n apply --translations TRANSLATIONS [--translations TRANSLATIONS ...] [OPTIONS] FILE
In version 0.9.0 i18n became a command with subcommands: apigenie i18n FILE --output DEST is now
apigenie i18n extract FILE --output DEST.
A typical workflow:
apigenie i18n extract openapi.yaml --output i18n-en.jsonwrites the texts of the spec.- Translators turn
i18n-en.jsonintoi18n-pl.json, changing the values and keeping the keys. apigenie i18n check openapi.yaml --translations i18n-pl.jsonfails, in CI for instance, until every text is translated and nothing else is.apigenie i18n apply openapi.yaml --translations i18n-pl.json --output openapi.pl.yamlwrites the Polish spec.- When the spec changes,
apigenie i18n sync openapi.yaml --previous i18n-en.json --translations i18n-pl.json --output i18n-pl.jsonkeeps the translations of the unchanged texts and lists the new texts, the changed ones (their translations may be stale) and the removed ones; theni18n extractwrites the newi18n-en.json.
The translation files can also be given to
apigenie-generator docs with --i18n pl=i18n-pl.json,
which writes a translated copy of each document into the documentation site, with a locale
switcher, without writing a translated spec.
| Input | Recognized by | Keys | Texts |
|---|---|---|---|
| OpenAPI 3.0, 3.1, 3.2 | the openapi property (JSON or YAML) |
JSON pointers | titles, summaries, descriptions, names |
| Swagger 2.0 | the swagger property |
JSON pointers | titles, summaries, descriptions, names |
| AsyncAPI 3.x | the asyncapi property |
JSON pointers | titles, summaries, descriptions, names |
| Protobuf | the .proto extension |
fully qualified names | leading comments |
| GraphQL | the .graphql, .graphqls or .gql extension |
schema coordinates and names | descriptions |
i18n extract
This command extracts texts suitable for translations, from an OpenAPI,
Swagger 2.0 or AsyncAPI 3.x document, or from a Protobuf or GraphQL file. Files with the .proto
extension are read as Protobuf files (see Protobuf files), and files
with the .graphql, .graphqls or .gql extension as GraphQL files (see
GraphQL files); the kind of any other document is detected by its
openapi, swagger or asyncapi property. Extracted texts are written in key-value files. For
OpenAPI, Swagger and AsyncAPI, keys are JSON pointers to locations in the input file, and values
are the strings the keys point to. For Protobuf and GraphQL files, keys are the full
names of the documented declarations (see below).
Example:
apigenie i18n extract --output i18n-en.json openapi-in.yaml
may produce the following output:
/info/title: My API
/info/contact/name: My Team
The command extracts the following properties of OpenAPI documents:
| Object | Properties |
|---|---|
Info |
title, summary (3.1+), description |
Contact |
name |
License |
name |
Server |
name (3.2), description |
ServerVariable |
description |
PathItem |
summary, description |
Schema |
title, description |
Response |
description |
Parameter |
description |
Example |
summary, description |
RequestBody |
description |
Header |
description |
SecurityScheme |
description |
OAuthFlow |
the description of each scope, keyed .../scopes/<scope> |
Link |
description |
Operation |
summary, description |
ExternalDocumentation |
description |
Tag |
summary (3.2), description |
| Reference Object | summary, description (3.1+): they override those of the referenced object at the place of the $ref only, so they are keyed there |
Schemas are extracted wherever they are, including under $defs, dependentSchemas and
additionalItems (definitions is not an OpenAPI 3.1 keyword, and is skipped). Identifiers and
values that are part of the API’s meaning are never extracted: names of tags, parameters,
properties and scopes, operationId, URLs, enum values, defaults, examples, xml.name and
$comment.
the following properties of Swagger 2.0 documents, as for OpenAPI:
| Object | Properties |
|---|---|
Info |
title, description |
Contact |
name |
License |
name |
Operation |
summary, description |
Parameter |
description |
Response |
description |
Header |
description |
Schema |
title, description |
SecurityScheme |
description |
ExternalDocumentation |
description |
Tag |
description |
and the following properties of AsyncAPI documents:
| Object | Properties |
|---|---|
Info |
title, description |
Contact |
name |
License |
name |
Server |
title, summary, description |
ServerVariable |
description |
Channel |
title, summary, description |
Parameter |
description |
Operation |
title, summary, description |
OperationTrait |
title, summary, description |
OperationReplyAddress |
description |
Message |
title, summary, description |
MessageTrait |
title, summary, description |
MessageExample |
summary |
CorrelationId |
description |
Schema |
title, description |
SecurityScheme |
description |
ExternalDocumentation |
description |
Tag |
description |
Texts are extracted where they are defined. An object used through a
reference is extracted once, under components for instance, and objects
kept in other files are not extracted; bundle the document first
(apigenie bundle) to extract them too. Schemas in formats other than
AsyncAPI and JSON Schema, such as Avro, and specification extensions are
skipped.
Protobuf files
The texts of a Protobuf file are the leading comments of its declarations:
the comment block right above a declaration, as protoc attaches it. Keys are
fully qualified names instead of JSON pointers:
| Declaration | Key |
|---|---|
The file (the comment of its syntax statement) |
the package, e.g. acme.store.v1 |
| Message, enum, service | acme.store.v1.Order |
| Field, map field, oneof | acme.store.v1.Order.id |
| Field of a oneof | the key of a field of the message, acme.store.v1.Order.card |
| Enum value | under its enum, acme.store.v1.Order.Status.STATUS_OPEN |
| Method | acme.store.v1.OrderService.CreateOrder |
| Extension field | in the scope of its extend block, acme.store.v1.max_length |
For example, the file
syntax = "proto3";
package acme.greeter.v1;
// Greets people.
service Greeter {
// Returns a greeting for one person.
rpc SayHello(HelloRequest) returns (HelloReply);
}
gives
{
"acme.greeter.v1.Greeter": "Greets people.",
"acme.greeter.v1.Greeter.SayHello": "Returns a greeting for one person."
}
Comment markers are removed, as are one space at the start of each line, the whitespace at the end of each line, and empty lines at the start and the end of a comment. Line breaks inside a comment are kept. In a file without a package, the keys start with the top-level names and the file comment has the empty key.
Trailing comments, detached comments (separated from the declaration by an
empty line), and comments on the package, imports, options, extend blocks and
reserved statements are not extracted. Imported files are not read.
GraphQL files
The command extracts the descriptions of GraphQL definitions, keyed by their full names:
| Definition | Key | Example |
|---|---|---|
| Schema | schema |
schema |
| Scalar, object, interface, union, enum and input object type | type name | Book |
| Field of an object or interface type | Type.field |
Book.title |
| Argument of a field | Type.field(argument:) |
Query.search(text:) |
| Field of an input object type | Input.field |
BorrowInput.days |
| Enum value | Enum.VALUE |
Genre.POETRY |
| Directive | @directive |
@key |
| Argument of a directive | @directive(argument:) |
@key(fields:) |
| Operation | operation type and name | query FindBooks |
| Variable of an operation | operation($variable:) |
query FindBooks($text:) |
| Fragment | fragment Name |
fragment BookSummary |
Type system keys are the schema coordinates of the GraphQL specification. Fields, arguments and
enum values of type extensions (extend type) are named after the extended type. An anonymous
operation is keyed by its operation type only, e.g. query. Empty descriptions are skipped, and
the document must be valid: duplicate names are reported as errors. For example,
apigenie i18n extract --output i18n-en.yaml schema.graphql
may produce:
Query: The entry points of the library API
Query.search(text:): The words to look for
schema: The library service
Options
-o, –output DEST
Name of the file with extracted texts. Format of the file is determined by its
extension: .json, or .yaml (.yml). Without it nothing is written: the file is only read and
its problems reported.
i18n apply
This command replaces the texts of a document by their translations and writes the translated
document. A translation file has the format i18n extract writes, in JSON or YAML: an object
whose keys are those i18n extract gives for the document and whose values are the translated
texts. Applying the texts i18n extract gives, untranslated, changes nothing.
apigenie i18n apply --translations i18n-pl.json openapi.yaml --output openapi.pl.yaml
- A key that addresses nothing in the document (a key of an older version of the document, a
mistyped one) is reported as a warning, and the other keys are applied. With
--strict, it is an error, and nothing is written. - The translation files can be repeated: they are applied in order, so the translations of later files override those of earlier ones.
- Comments are kept, unless
--strip-commentsis given. - A file that can’t be read or parsed, or a translation that can’t be applied (see below), is an error, and nothing is written.
OpenAPI, Swagger and AsyncAPI specifications
The keys are JSON pointers into the document (JSON or YAML). With i18n-pl.json
{
"/info/title": "Moje API",
"/paths/~1pets/get/summary": "Lista zwierząt"
}
the title of the API and the summary of GET /pets are translated. Everything else is
kept as it is: other values, the order of properties, and the YAML comments of the
document, those of the translated texts included.
- Only the texts
i18n extractextracts are translated. A key pointing anywhere else, to anoperationId, a URL, an object or a value that is not a string, addresses nothing: it is reported as a warning (an error with--strict), and the value is kept. The document must be a valid specification, so that its texts can be extracted. - Keys that are not JSON pointers, such as those of Protobuf and GraphQL files, and translations that are not strings, are errors.
- References are not followed: a text used through a
$refis translated where it is defined, asi18n extractextracts it there.
Protobuf files
The keys are the fully qualified names i18n extract gives, and each translation replaces the
leading comment of its declaration (the comment of the syntax statement for the package key):
apigenie i18n apply --translations i18n-de.json greeter.proto --output greeter.de.proto
The comment is rewritten in place, in its style: // lines stay // lines with their
indentation, and a /* */ comment stays one, on one line if it was and the translation fits on
one, as a /*, * line, */ block otherwise. A translation containing */ is written as //
lines, and an empty translation removes the comment. Nothing else changes: layout, trailing and
detached comments, and the comments without a translation are kept byte for byte. A key naming a
declaration without a leading comment addresses nothing.
GraphQL files
The keys are the schema coordinates and names i18n extract gives, and each translation
replaces the description of its definition:
{
"Query": "Die Einstiegspunkte der Bibliotheks-API",
"Query.search(text:)": "Die zu suchenden Wörter"
}
The description is rewritten in place, in its form: a block string ("""...""") stays one, on
one line if it was and the translation fits on one, on lines of their own at the indentation of
the original otherwise; a quoted string ("...") stays quoted, with escapes. A translation a
block string can’t hold (control characters, a common indentation of all lines) is quoted.
Everything else is kept byte for byte, # comments included. A key naming a definition without a
description addresses nothing.
Options
–translations TRANSLATIONS
Path to a translation file, in JSON or YAML. Required. It can be repeated: the files are applied in order, so the translations of later files override those of earlier ones.
-o, –output OUTPUT
Path to the translated document. Defaults to the standard output.
–format FORMAT
Format of a translated specification, json or yaml. Defaults to the format of the input. Not
available for Protobuf and GraphQL files, which are written in their own format.
–strict
Fails, without writing anything, when a key of a translation file addresses nothing in the document.
–strip-comments
Drops the comments of the document instead of keeping them: the YAML comments of a
specification, the # comments of a GraphQL file (descriptions are kept, they are part of the
schema). Not available for Protobuf files, whose translated texts are comments.
i18n check
This command compares a translation file with the texts of a document, and fails unless every text has a translation and every key of the file is a text. It is meant for CI: run it before publishing a translation.
apigenie i18n check openapi.yaml --translations i18n-pl.json
FILE is a spec, a Protobuf or GraphQL file, whose texts are extracted as i18n extract does,
or a file i18n extract wrote. The command reports, as errors:
- the texts without a translation, in the order of the texts;
- the texts whose translation is empty, unless
--allow-emptyis given; - the keys that are not texts of the document: a key of an older version of the document, a
mistyped one, or one pointing to a value that is not meant to be translated, such as an
operationId; - the keys that are not JSON pointers (for specs), and the translations that are not strings.
It then writes, on the standard output, how many texts are translated:
i18n-pl.json: 74 of 76 texts translated (97.4%).
A text whose source changed after it was translated still counts as translated: i18n sync
finds those.
Options
–translations TRANSLATIONS
Path to the translation file, in JSON or YAML. Required.
–allow-empty
Counts empty translations ("") as translated, for texts meant to be empty in the language.
–format FORMAT
text (the default) or json. With json, nothing is written to the standard error, and the
standard output is an object:
{
"complete": false,
"translated": 74,
"total": 76,
"coverage": 0.9736842105263158,
"missing": ["/info/description", "/paths/~1pets/get/summary"],
"empty": [],
"unknown": [],
"invalid": []
}
i18n sync
This command updates a translation file after the document changed. Keys are locations, not
texts: when a description changes, its key stays the same, and the old translation would still
look complete. i18n sync compares the texts the translations were made from (--previous)
with the current ones (FILE), and:
- keeps the translations of the unchanged texts;
- reports the texts whose source changed, as warnings: review their translations, which may be
stale. With
--drop-changed, their translations are dropped, so thati18n checkreports them as missing; - reports the translations of removed texts, and keeps them unless
--pruneis given; - counts the new texts, which have no translation yet.
apigenie i18n sync openapi.yaml --previous i18n-en.json --translations i18n-pl.json -o i18n-pl.json
FILE and PREVIOUS are specs, Protobuf or GraphQL files, or files i18n extract wrote: keeping
the i18n extract output of the version the translators worked on is enough. Texts are matched by
key, so a text that moved, such as a renamed path, is removed at its old key and added at its new
one. The output follows the order of the current texts, then the kept translations of removed
texts. Keys that are not JSON pointers, and values that are not strings, are copied at the end.
Options
–previous PREVIOUS
The texts the translations were made from. Required.
–translations TRANSLATIONS
Path to the translation file to update, in JSON or YAML. Required.
-o, –output OUTPUT
Path to the updated translation file; its format follows its extension. With it, the command
writes a summary (3 new texts, 2 changed, 1 removed, 70 unchanged.) on the standard output.
Defaults to the standard output, in the format of the translation file.
–prune
Drops the translations of the texts the document no longer has.
–drop-changed
Drops the translations of the texts that changed, instead of keeping them for review.
Exit status
The subcommands exit with 0 on success, warnings about unknown keys and changed texts included.
They exit with 1 when i18n check finds the translation file incomplete, or when, with --strict,
a key of i18n apply addresses nothing. They exit with 2 when the input or a translation file
can’t be read or parsed, a translation can’t be applied, an option isn’t available for the input
(--format for Protobuf and GraphQL files, --strip-comments for Protobuf files), or i18n is
run without a subcommand. See the exit statuses shared by the three binaries.
See also
- bundle, to put the texts of referenced files into the document before extracting them
- apigenie-generator docs, which takes the translation files with
--i18n