Skip to content

Commit

Permalink
i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
bsrdjan committed Jan 31, 2021
1 parent 6c94ec6 commit f8dc7c3
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 30 deletions.
50 changes: 49 additions & 1 deletion abap-api-tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Command line tool for pattern based applications with ABAP/HANA systems.
- [ABAP API annotations for ui elements](#abap-api-annotations-for-ui-elements)
- [ui elements](#ui-elements)
- [Custom ui configurations](#custom-ui-configurations)
- [i18n](#i18n)
- [Known Issues](#known-issues)
- [Getting Support](#getting-support)
- [Contributing](#contributing)
Expand Down Expand Up @@ -269,7 +270,7 @@ datepicker: >-
You can edit both config files and use them with `make` command:

```shell
abap make my-ui5 -c planned_order_api
abap make my-ui5 -c my-api
```

Elements with tilde prefix `~` are placeholders for texts, data binding and value input helps, described in standard ui configuration `yaml` file.
Expand All @@ -280,6 +281,53 @@ Custom configuration with the same name as standard one, if present in local fol
abap rm my-ui5
```

## i18n

Texts for i18n translations are saved in `texts.yaml`, for the language used in `get` command:

```shell
abap get MME -c my-api # default lang = en
```

`texts.yaml`

```yaml
en: City postal code
short:
en:
FIELDTEXT: City postal code
REPTEXT: Postl Code
SCRTEXT_L: Postal Code
SCRTEXT_M: Postal Code
SCRTEXT_S: Postl Code
```

Texts in additional languages are added using `-t|--text-only` boolean option:

```shell
abap get MME -c my-api --text-only --lang de
```

`texts.yaml`

```yaml
de: Postleitzahl des Orts
en: City postal code
short:
de:
FIELDTEXT: Postleitzahl des Orts
REPTEXT: PLZ
SCRTEXT_L: Postleitzahl
SCRTEXT_M: Postleitzahl
SCRTEXT_S: PLZ
en:
FIELDTEXT: City postal code
REPTEXT: Postl Code
SCRTEXT_L: Postal Code
SCRTEXT_M: Postal Code
SCRTEXT_S: Postl Code
```

## Known Issues

Click [here](https://github.com/SAP/fundamental-toolset/issues) to view the current issues.
Expand Down
4 changes: 2 additions & 2 deletions abap-api-tools/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion abap-api-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "abap-api-tools",
"description": "ABAP api tools",
"version": "1.1.0",
"version": "1.2.0",
"homepage": "https://github.com/sap/fundamental-tools",
"author": "SAP",
"license": "Apache-2.0",
Expand Down
12 changes: 11 additions & 1 deletion abap-api-tools/src/ts/abap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface Arguments {
output: string;
lang: string;
save: boolean;
textOnly: boolean;
}

class CliHandler {
Expand Down Expand Up @@ -89,7 +90,10 @@ class CliHandler {
abap = await backend.parse();
}

if ([Command.call, Command.get, Command.make].includes(this.argv.cmd)) {
if (
(this.argv.cmd === Command.get && !this.argv.textOnly) ||
[Command.call, Command.make].includes(this.argv.cmd)
) {
const frontend = new Frontend(api_name, abap, this.argv);
log.debug(`frontend run ${api_name}`);
frontend.parse();
Expand Down Expand Up @@ -197,6 +201,12 @@ export const argv = yargs(process.argv.slice(2))
alias: "catalog",
describe: "Read RFM names from file",
})
.option("t", {
alias: "text-only",
type: "boolean",
default: false,
describe: "Get only texts in a given language",
})
.option("o", {
alias: "output",
describe: "Output folder",
Expand Down
129 changes: 104 additions & 25 deletions abap-api-tools/src/ts/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export type ParameterType = {
};
type yamlParameters = Record<string, Record<string, ParameterType>>;

export type FieldText = {
export type FieldTextType = {
FIELDTEXT?: string;
REPTEXT?: string;
SCRTEXT_S?: string;
Expand Down Expand Up @@ -67,13 +67,15 @@ export type FieldType = {
MEMORYID?: string;
SHLP?: string;
};
text: FieldText;
text: FieldTextType;
};

export type StructureType = Record<string, FieldType>;

export type yamlFields = Record<string, FieldType | StructureType>;

export type yamlTexts = Record<string, Record<string, string | FieldTextType>>;

// AbapObject typings

type FieldValuesHelpType = {
Expand Down Expand Up @@ -173,10 +175,10 @@ export class Backend {
private SPRAS: string;
private Helps: Helps;
private Stat: Stat;
private Texts = {} as yamlTexts;

private client: Client;

// tslint:disable:no-empty
constructor(api_name: string, argv: Arguments) {
this.argv = argv;
this.api_name = api_name;
Expand Down Expand Up @@ -223,18 +225,20 @@ export class Backend {
}
}

getDfiesText(dfies: RfcStructure): FieldText {
getDfiesText(dfies: RfcStructure): FieldTextType {
const TEXT_FIELDS = [
"FIELDTEXT",
"REPTEXT",
"SCRTEXT_S",
"SCRTEXT_M",
"SCRTEXT_L",
];
const text: FieldText = {};
const text: FieldTextType = {};
TEXT_FIELDS.forEach((t) => {
if ((dfies[t] as string).length > 0) text[t] = dfies[t];
});
// todo: remove this eventually
if (!text.FIELDTEXT) text.FIELDTEXT = "No field text";
return text;
}

Expand Down Expand Up @@ -498,6 +502,31 @@ export class Backend {
if ((field.INTTYPE as string).trim() && !field[".INCLUDE"]) {
this.alpha.field(field.FIELDNAME as string);
result[field.FIELDNAME as string] = await this.getField(field);

// Field texts -> Texts
const tkey = JSON.stringify({
t: param.TABNAME as string,
f: field.FIELDNAME as string,
});
const texts = result[field.FIELDNAME as string].text;
if (this.argv.textOnly) {
for (const [k, v] of Object.entries(this.Texts)) {
if (v._id === tkey) {
this.Texts[k][this.argv.lang] = texts.FIELDTEXT as string;
this.Texts[k].short[this.argv.lang] = texts;
break;
}
}
// if not found, the same text already added by another field
} else {
if (!this.Texts[texts.FIELDTEXT as string]) {
this.Texts[texts.FIELDTEXT as string] = {
_id: tkey,
[this.argv.lang]: texts.FIELDTEXT as string,
short: { [this.argv.lang]: texts },
};
}
}
}
}
return result;
Expand All @@ -522,7 +551,19 @@ export class Backend {
}

async parse(): Promise<AbapObject> {
this.annotations_clean();
if (!this.argv.textOnly) {
this.annotations_clean();
} else {
// Texts
if (this.argv.textOnly) {
const folder_yaml = this.api_name
? path.join(this.argv.output, this.api_name, "yaml")
: path.join(this.argv.output, "yaml");
this.Texts = yamlLoad(
path.join(folder_yaml, "texts.yaml")
) as yamlTexts;
}
}

log.info(
`\napi ${this.argv.dest} ${chalk.bold(this.api_name)} language: ${
Expand Down Expand Up @@ -561,8 +602,8 @@ export class Backend {
continue;
}
// more intuitive names
p.functionName = p["FUNCNAME"].trim() as string;
p.paramName = p["PARAMETER"].trim() as string;
p.functionName = (p["FUNCNAME"] as string).trim();
p.paramName = (p["PARAMETER"] as string).trim();

// Trim, jusr for any case
p.FIELDNAME.trim();
Expand Down Expand Up @@ -603,7 +644,6 @@ export class Backend {
}

// Sort by rfm / parameter class / required/optional / parameter type and name

(R["PARAMETERS"] as RfcTable).sort((a: RfcStructure, b: RfcStructure) => {
const PClass = ["I", "C", "T", "E", "X"];
const PType = [
Expand Down Expand Up @@ -658,6 +698,25 @@ export class Backend {
// stat
this.Stat[functionName][p.paramType as string]++;

// Parameter text -> Texts
const tkey = JSON.stringify({ r: p.functionName, p: p.paramName });
if (this.argv.textOnly) {
for (const [k, v] of Object.entries(this.Texts)) {
if (v._id === tkey) {
this.Texts[k][this.argv.lang] = p.PARAMTEXT as string;
break;
}
}
// if not found, the same text already added by another parameter
} else {
if (!this.Texts[p.PARAMTEXT as string]) {
this.Texts[p.PARAMTEXT as string] = {
_id: tkey,
[this.argv.lang]: p.PARAMTEXT as string,
};
}
}

//
// dfies
//
Expand Down Expand Up @@ -750,36 +809,52 @@ export class Backend {
usage: usage,
};

if (this.argv._[0] === Command.get) {
this.annotations_write(abap);
if (this.argv.cmd === Command.get) {
this.annotations_write(abap, this.Texts, this.argv.textOnly);
}

return abap;
}

annotations_write(abap: AbapObject): void {
annotations_write(
abap: AbapObject,
texts: yamlTexts,
textOnly: boolean
): void {
const folder_root: string = path.join(
this.argv.output as string,
this.api_name
);
const folder_yaml: string = path.join(folder_root, "yaml");
log.debug(`Annotations save ${folder_yaml}`);
log.debug(
`Annotations save ${folder_yaml} ${textOnly ? "only texts" : ""}`
);

if (!fs.existsSync(folder_root)) {
fs.mkdirSync(folder_root);
}
if (!fs.existsSync(folder_yaml)) {
fs.mkdirSync(folder_yaml);
}

yamlSave(`${folder_yaml}/alpha.yaml`, abap.alpha, { sortKeys: true });
yamlSave(`${folder_yaml}/parameters.yaml`, abap.parameters);
yamlSave(`${folder_yaml}/fields.yaml`, abap.fields);
if (abap.helps) {
yamlSave(`${folder_yaml}/helps.yaml`, abap.helps, { sortKeys: true });
if (!textOnly) {
yamlSave(path.join(folder_yaml, "alpha.yaml"), abap.alpha, {
sortKeys: true,
});
yamlSave(path.join(folder_yaml, "parameters.yaml"), abap.parameters);
yamlSave(path.join(folder_yaml, "fields.yaml"), abap.fields);
if (abap.helps) {
yamlSave(path.join(folder_yaml, "helps.yaml"), abap.helps, {
sortKeys: true,
});
}
yamlSave(path.join(folder_yaml, "stat.yaml"), abap.stat);
yamlSave(path.join(folder_yaml, "usage.yaml"), abap.usage);
}
if (!isEmpty(texts)) {
yamlSave(path.join(folder_yaml, "texts.yaml"), texts, {
sortKeys: true,
});
}
yamlSave(`${folder_yaml}/stat.yaml`, abap.stat);
yamlSave(`${folder_yaml}/usage.yaml`, abap.usage);
}

annotations_clean(): void {
Expand All @@ -790,15 +865,17 @@ export class Backend {
);

log.debug(`Annotations clean ${folder_yaml}`);

for (const fileName of [
"parameters",
"fields",
"helps",
"stat",
"alpha",
"usage",
"texts",
]) {
deleteFile(`${folder_yaml}/${fileName}.yaml`);
deleteFile(path.join(folder_yaml, `${fileName}.yaml`));
}
}
}
Expand All @@ -814,8 +891,10 @@ export function annotations_read(
log.debug(`reading annotations for: ${api_name} from ${folder_yaml}`);

return {
parameters: yamlLoad(`${folder_yaml}/parameters.yaml`) as yamlParameters,
fields: yamlLoad(`${folder_yaml}/fields.yaml`) as yamlFields,
stat: yamlLoad(`${folder_yaml}/stat.yaml`) as Stat,
parameters: yamlLoad(
path.join(folder_yaml, "parameters.yaml")
) as yamlParameters,
fields: yamlLoad(path.join(folder_yaml, "fields.yaml")) as yamlFields,
stat: yamlLoad(path.join(folder_yaml, "stat.yaml")) as Stat,
};
}

0 comments on commit f8dc7c3

Please sign in to comment.