Skip to content

Commit

Permalink
first one
Browse files Browse the repository at this point in the history
  • Loading branch information
tzdesign committed Oct 6, 2023
0 parents commit 13add33
Show file tree
Hide file tree
Showing 16 changed files with 765 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Publish to NPM
on:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: "16.x"
registry-url: "https://registry.npmjs.org"
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Install dependencies and build 🔧
run: yarn install --frozen-lockfile && yarn build
- name: Publish package on NPM 📦
run: yarn publish --new-version $(echo "${{ github.event.release.tag_name }}" | sed "s/v//") --non-interactive --no-commit-hooks --no-git-tag-version
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
**/.DS_Store
*-debug.log
*-error.log
/.idea
/.nyc_output
/dist
/lib
/package-lock.json
/tmp
node_modules
src/test/*
!src/test/en-US.json
11 changes: 11 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
**/.DS_Store
*-debug.log
*-error.log
/.idea
/.nyc_output
/lib
/package-lock.json
/tmp
/src
/.github
tsconfig.json
77 changes: 77 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# deepl $localize tranlate

This tool uses deepl to translate all extracted languages by angular/localize.

## About

### What is deepl?

DeepL is a machine translation company that offers neural machine translation services. Neural machine translation (NMT) is a type of machine translation that uses artificial neural networks to improve the accuracy and fluency of translations. DeepL is known for its high-quality translations and is considered one of the leading providers in the field of machine translation.

DeepL's most well-known product is the DeepL Translator, which allows users to translate text and documents between multiple languages. It supports a wide range of languages and is often praised for its ability to produce translations that are more natural and contextually accurate compared to traditional rule-based machine translation systems.

[See deepl](http://deepl.com/)

### What is $localize?

The [$localize ](https://angular.io/api/localize/init/localize)function is part of the [Angular framework](https://angular.io/), which is a popular open-source JavaScript framework for building web and mobile applications. It is specifically used for internationalization (i18n) and localization (l10n) purposes within Angular applications.

The $localize function is used to mark and translate text within an Angular application. It allows developers to define messages in different languages and provides a way to extract those messages for translation. Here's how it typically works:

1. Developers use the $localize function to mark text in their Angular templates and components that need to be translated. For example:
```tsx
const message = $localize`Hello, World!`;
```
2. After marking the text, developers can use Angular's localization tools to extract these marked messages into a translation file.
3. Translators can then provide translations for the marked messages in various languages.
4. When the application runs, Angular will use the appropriate translation based on the user's language preference or the selected language.

Keep in mind that Angular's internationalization and localization features, including $localize, may evolve over time, so it's a good practice to refer to the official Angular documentation or resources for the most up-to-date information on how to use these features in your Angular applications.

> Localize perfectly works with other frameworks as well. See [Miško's approach](https://github.com/mhevery/qwik-i18n) for [qwik](https://qwik.builder.io/).
## Installation

```shell
yarn add deepl-localize
```

## Translation

### Usage
```shell
deepl-localize translate -b your/path/en-US.json -l de-DE fr-FR -i de-DE -a "YOUR-DEEPL-API-KEY"
```

### Options:

| Command | Description |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| -a,--api-key <value> | The deepl api key. If none is given, the environment variable DEEPL_API_KEY is used. |
| -v, --version | output the version number |
| -b, --base <value> | The base file path. |
| -o, --output <value> | The output folder path. If none is given, the base folder is used. |
| -l, --locales [value...] | Locales to translate to. For example de-DE,fr-FR. If none is given, no translation will happen. (default: []) |
| -i, --informal-locales [value...] | Locales to translate less formal. For example de-DE will use du instead of sie. (default: []) |
| -h, --help | display help for command |


## Translation

### Usage
```shell
deepl-localize remove-stale -b your/path/en-US.json -l de-DE fr-FR
```

### Options:

| Command | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------- |
| -v, --version | output the version number |
| -b, --base <value> | The base file path. |
| -o, --output <value> | The output folder path. If none is given, the base folder is used. |
| -l, --locales [value...] | Locales to translate to. For example de-DE,fr-FR. If none is given, no translation will happen. (default: []) |
| -d,--dry-run | Just show the stale translations. The script will not remove and just show them. (default: false) |
| -h, --help | display help for command |
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "deepl-localize",
"description": "Translation for $localize files with deepl",
"main": "dist/index.js",
"author": "Tobias Zimmermann",
"repository": {
"url": "https://github.com/tzdesign/deepl-localize"
},
"license": "MIT",
"dependencies": {
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"commander": "^11.0.0",
"deepl-node": "^1.10.2",
"figlet": "^1.6.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/cli-progress": "^3.11.3",
"@types/figlet": "^1.5.6",
"@types/node": "^20.8.2",
"tsc": "^2.0.4",
"typescript": "^5.2.2"
},
"scripts": {
"build": "tsc",
"test:translate": "tsc && node dist/index.js translate -b src/test/en-US.json -l de-DE de-CH fr-FR sk-SK -i de-DE",
"test:stale": "tsc && node dist/index.js remove-stale -b src/test/en-US.json -l de-DE de-CH",
"test:drystale": "tsc && node dist/index.js remove-stale -d -b src/test/en-US.json -l de-DE de-CH"
},
"bin": {
"deepl-localize": "./dist/index.js"
},
"version": "1.0.0"
}
44 changes: 44 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node
import { Command } from "commander";
import figlet from "figlet";
import translate from "./translate";
import "colors";
import { appendTranslationOption } from "./translate/translationOptions";
import stale from "./stale";

const program = new Command();

console.log(
figlet.textSync("Deepl $localize", { horizontalLayout: "full" }).rainbow
);

program.version("1.0.0").description("Translate $localize files");

appendTranslationOption(program.command("remove-stale"))
.option(
"-d,--dry-run",
"Just show the stale translations. The script will not remove them.",
false
)
.action(stale);

appendTranslationOption(program.command("translate"))
.option(
"-i, --informal-locales [value...]",
"Locales to translate less formal. For example de-DE will use du instead of sie.",
[]
)
.option(
"-a,--api-key <value>",
"The deepl api key. If none is given, the environment variable DEEPL_API_KEY is used."
)
.action(translate);

program.parse(process.argv);

process.on("uncaughtException", function (err: Error) {
console.error("Error: ".red + err.message);
if (process.env.DEBUG) {
console.error(err);
}
});
99 changes: 99 additions & 0 deletions src/stale/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Command } from "commander";
import { TranslationOptions } from "../translate/translationOptions";
import outputDir from "../translate/outputDir";
import retrieveBase from "../translate/retrieveBase";
import { localizeFileSchema, validateBase } from "../translate/validateBase";
import fs, { write } from "fs";
import "colors";
import { readMeta, writeMeta } from "../translate/meta";

export type StaleOptions = Omit<TranslationOptions, "informalLocales"> & {
dryRun: boolean;
};

export default async function stale(this: Command) {
const options = this.opts<StaleOptions>();
console.log(`Your settings:`);
console.log(`\tBase: ${options.base}`);
console.log(`\tOutput: ${outputDir(options.base ?? "", options.output)}`);
console.log(`\tLocales: ${options.locales.join(", ")}`);
console.log("");

if (options.base === undefined) throw new Error("No base path given.");

if (options.locales.length === 0)
throw new Error("No locales as targets given.");

const base = retrieveBase(options.base);
if (base === undefined) throw new Error("No valid base file given.");
const baseData = validateBase(base);
const baseKeys = Object.keys(baseData.translations);

for (const targetLocaleString of options.locales) {
try {
let currentTranslations = localizeFileSchema.parse(
JSON.parse(
fs
.readFileSync(
`${outputDir(
options.base ?? "",
options.output
)}/${targetLocaleString}.json`
)
.toString()
)
);

const staleTranslations = Object.entries(
currentTranslations.translations
).filter(([key]) => baseKeys.includes(key) === false);

if (staleTranslations.length === 0) {
continue;
}

if (options.dryRun) {
console.log(
`${
"Dry run:".red
} Stale translations for ${targetLocaleString}:\n${staleTranslations
.map(([key, value]) => `\t${key} (${value})`)
.join("\n")}\n`
);
} else {
for (const [key] of staleTranslations) {
delete currentTranslations.translations[key];
}
fs.writeFileSync(
`${outputDir(
options.base ?? "",
options.output
)}/${targetLocaleString}.json`,
JSON.stringify(currentTranslations, null, 2)
);
console.log(
`Deleted stale translations for ${targetLocaleString}:\n${staleTranslations
.map(([key, value]) => `\t${key} (${value})`.red)
.join("\n")}\n`
);
}
} catch (error) {
console.error(
`Could not find target locale file for ${targetLocaleString}.`.red
);
}
}

// Clean up meta

let meta = readMeta(outputDir(options.base ?? "", options.output));

for (const [locale, translations] of Object.entries(meta)) {
for (const [key] of Object.entries(translations)) {
if (baseKeys.includes(key) === false) {
delete meta[locale][key];
}
}
}
writeMeta(meta, outputDir(options.base ?? "", options.output));
}
12 changes: 12 additions & 0 deletions src/test/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"locale": "en-US",
"translations": {
"1123557981657403007": "Start game",
"3344064097523459939": "Game settings",
"1063462873103909661": "Difficulty",
"64920563132254451162": "This is awesome",
"64920563132654451162": "I wish you good luck and a happy new year",
"6492056313265451162": "You are able to setup your address here",
"823262032963814625": "From {$unit} - {$discount}% Discount"
}
}
Loading

0 comments on commit 13add33

Please sign in to comment.