@translation/angular
Advanced tools
Comparing version 1.0.0 to 1.0.1
# Changelog | ||
## [v1.00](https://github.com/translation/angular/releases/tag/v1.00) (2022-09-16) | ||
## [v1.0.1](https://github.com/translation/angular/releases/tag/v1.0.1) (2022-09-29) | ||
#### New features: | ||
* First working complete version of this package | ||
* Requests now send `client` (angular) and `version` of the package, to be able to manage deprecated versions server-side. | ||
## [v1.0.0](https://github.com/translation/angular/releases/tag/v1.0.0) (2022-09-26) | ||
#### New features: | ||
* First working complete version of this package. | ||
{ | ||
"name": "@translation/angular", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "Translation.io client for Angular applications", | ||
@@ -5,0 +5,0 @@ "repository": { |
158
README.md
@@ -5,2 +5,4 @@ # [Translation.io](https://translation.io/angular) client for Angular | ||
[![Build Status](https://github.com/translation/angular/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/translation/angular/actions/workflows/test.yml) | ||
[![Test Coverage](https://api.codeclimate.com/v1/badges/52c0801558cd21fa31dd/test_coverage)](https://codeclimate.com/github/translation/angular/test_coverage) | ||
[![npm version)](https://img.shields.io/npm/v/@translation/angular)](https://www.npmjs.com/package/@translation/angular) | ||
@@ -24,3 +26,3 @@ Add this package to localize your Angular application (see [Installation](#installation)). | ||
* [i18n attribute in templates](#i18n-attribute-in-templates) | ||
* [$localize for literal string in code](#$localize-for-literal-string-in-code) | ||
* [$localize in classes and functions](#$localize-in-classes-and-functions) | ||
* [Installation](#installation) | ||
@@ -47,23 +49,36 @@ * [Usage](#usage) | ||
```html | ||
<h1 i18n>Welcome to our Angular app!</h1> | ||
<p i18n>This is a first paragraph.</p> | ||
``` | ||
<!-- Simple use of the i18n attribute --> | ||
<h1 i18n>Welcome to our Angular application!</h1> | ||
Mark the attributes of HTML elements as translatable by using `i18n-{attribute_name}` attributes. | ||
<!-- Variable interpolation --> | ||
<!-- Translators will see "Hi {name}, welcome to your dashboard!" --> | ||
<p i18n>Hi {{ name }}, welcome to your dashboard!</p> | ||
```html | ||
// Marking an image's title attribute as translatable | ||
<img [src]="example-image" i18n-title title="Example image title" /> | ||
<!-- Simple HTML tags interpolation --> | ||
<!-- Translators will see "Text with <1>HTML</1> tags." --> | ||
<p i18n>Text with <em>HTML</em> tags.</p> | ||
<!-- Translatable attribute --> | ||
<img [src]="cat.png" i18n-alt alt="A fluffy cat" /> | ||
<!-- ICU plural form --> | ||
<!-- Translators will see "There are no cats", "There is one cat", "There are {x} cats" --> | ||
<p i18n>{count, plural, | ||
=0 {There are no cats} | ||
=1 {There is one cat} | ||
other {There are {{count}} cats} | ||
}</p> | ||
``` | ||
### $localize for literal strings in code | ||
### $localize in classes and functions | ||
Mark text (literal strings) as translatable in your component code using `$localize` and surrounding the text with backticks ( \` ). | ||
Mark text (literal strings) as translatable in your component classes and functions using `$localize` and surrounding the text with backticks ( \` ). | ||
```javascript | ||
// Marking a literal string as translatable | ||
$localize `Hello, we hope you will enjoy this app.`; | ||
// Simple use of the $localize function | ||
let text = $localize `Welcome to our Angular application!`; | ||
``` | ||
To explore the syntax more in details (using IDs, specifying plurals and interpolations), please check out the section [Localization syntax in details](#localization-syntax-in-details) below. | ||
To explore the syntax more in details (specifying metadata, using plurals and interpolations), please check out the "[Localization syntax in details](#localization-syntax-in-details)" section below. | ||
@@ -73,5 +88,5 @@ | ||
### 1. Install the localize package and set up your i18n options | ||
### 1. Check your Angular i18n configuration | ||
Make sure that you have Angular's [localize package](https://angular.io/guide/i18n-common-add-package) installed, or install it. | ||
Make sure that you have [Angular's localize package](https://angular.io/guide/i18n-common-add-package) installed, or install it. | ||
@@ -84,3 +99,3 @@ ```bash | ||
### 2. Install the `@translation/angular` package | ||
### 2. Install our `@translation/angular` package | ||
@@ -101,3 +116,3 @@ Run the following command at the root of your project to install our package: | ||
### 4. Copy the generated `tio.config.json` file to the root of your application. | ||
### 4. Copy the generated `tio.config.json` file to the root of your application | ||
@@ -109,3 +124,3 @@ This configuration file should look like this: | ||
"source_locale": "en", | ||
"target_locales": ["fr", "es", "it"] | ||
"target_locales": ["fr", "it", "es"] | ||
} | ||
@@ -121,2 +136,3 @@ ``` | ||
"scripts": { | ||
"extract": "ng extract-i18n --output-path=src/locale", | ||
"translation:init": "npm run extract && tio init", | ||
@@ -128,2 +144,4 @@ "translation:sync": "npm run extract && tio sync" | ||
N.B. If you are using Angular version 10 or lower, replace **extract-i18n** by **xi18n** in the "extract" command. | ||
### 6. Initialize your project | ||
@@ -215,4 +233,3 @@ | ||
```html | ||
<h1 i18n>Welcome to our Angular app!</h1> | ||
<p i18n>This is a first paragraph.</p> | ||
<h1 i18n>Welcome to our Angular application!</h1> | ||
``` | ||
@@ -223,13 +240,33 @@ | ||
```html | ||
// Marking the title attribute of an image as translatable | ||
<img [src]="example-image" i18n-title title="Example image title" /> | ||
<img [src]="cat.png" i18n-alt alt="A fluffy cat" /> | ||
``` | ||
Literal strings in your component code can also be marked as translatable using `$localize` and surrounding the source text with backticks ( \` ). | ||
You can interpolate variables (component properties) into translatable strings. | ||
```html | ||
<!-- Translators will see "Hi {name}, welcome to your dashboard!" --> | ||
<p i18n>Hi {{ name }}, welcome to your dashboard!</p> | ||
``` | ||
And you can also interpolate **valid** HTML tags. | ||
```html | ||
<!-- Translators will see "Text with <1>HTML</1> tags." --> | ||
<p i18n>Text with <em>HTML</em> tags.</p> | ||
<!-- Translators will see "Text with a <1><2>partly-emphasized</2> link</1>." --> | ||
<p i18n>Text with a <a href="#"><em>partly-emphasized</em> link</a>.</p> | ||
``` | ||
Literal strings in your component classes and functions can also be marked as translatable using `$localize` and surrounding the source text with backticks ( \` ). | ||
```javascript | ||
// Marking a literal string as translatable | ||
$localize `Hello, we hope you will enjoy this app.`; | ||
let text = $localize `Hello, we hope you will enjoy this app.`; | ||
``` | ||
This syntax also allows for variable interpolation. | ||
```javascript | ||
// Translators will see "Hi {name}, welcome to your dashboard!" | ||
let text = $localize `Hi ${name}, welcome to your dashboard!`; | ||
``` | ||
The official Angular documentation for the syntax can be found [here](https://angular.io/guide/i18n-common-prepare). | ||
@@ -241,12 +278,32 @@ | ||
The syntax for the metadata is the following: `{meaning}|{description}@@{custom_id}` | ||
The syntax for the metadata, is the following: `{meaning}|{description}@@{custom_id}` | ||
```html | ||
<h1 i18n="Welcome message|Message used on the homepage@@home-welcome-message">Welcome to our Angular app!</h1> | ||
<!-- Specifying only the meaning (the pipe | is required) --> | ||
<h1 i18n="Welcome message|">Welcome to our app!</h1> | ||
<!-- Specifying only the description --> | ||
<h1 i18n="Message used on the homepage">Welcome to our app!</h1> | ||
<!-- Specifying only the indetifier --> | ||
<h1 i18n="@@home-welcome-message">Welcome to our app!</h1> | ||
<!-- Specifying a meaning, a description and an identifier --> | ||
<h1 i18n="Welcome message|Message used on the homepage@@home-welcome-message">Welcome to our app!</h1> | ||
``` | ||
Metadata can also be used with `$localize`, but it must then be formatted as follows: `{meaning}:{description}@@{custom_id}:{source_text}`. | ||
Metadata can also be used with `$localize`, but it must then be formatted as follows: `:{meaning}|{description}@@{custom_id}:{source_text}`. | ||
```javascript | ||
let welcomeText = $localize `Welcome message:Message used on the homepage@@home-welcome-message:Welcome to our Angular app!`; | ||
// Specifying only the meaning (the pipe | is required) | ||
let text = $localize `:Welcome message|:Welcome to our Angular app!`; | ||
// Specifying only the description | ||
let text = $localize `:Message used on the homepage:Welcome to our Angular app!`; | ||
// Specifying only the identifier | ||
let text = $localize `:@@home-welcome-message:Welcome to our Angular app!`; | ||
// Specifying a meaning, a description and an identifier | ||
let text = $localize `:Welcome message|Message used on the homepage@@home-welcome-message:Welcome to our Angular app!`; | ||
``` | ||
@@ -266,24 +323,24 @@ | ||
```html | ||
// Good use cases: | ||
<!-- Good use cases: --> | ||
/* | ||
1. The meaning helps distinguish between two keys with the same source text | ||
-> This will result in two distinct source keys | ||
*/ | ||
<span i18n="Numbered day in a calendar|">Date</span> // Meaning only | ||
<span i18n="Social meeting with someone|">Date</span> // Meaning only | ||
<!-- Example 1 | ||
The meaning helps distinguish between two keys with the same source text | ||
=> This will result in two distinct source keys | ||
--> | ||
<span i18n="Numbered day in a calendar|">Date</span> | ||
<span i18n="Social meeting with someone|">Date</span> | ||
/* | ||
2. Adding a description after the meaning will be useful to translators | ||
-> This will result in two distinct source keys | ||
*/ | ||
<!-- Example 2 | ||
Adding a description after the meaning will be useful to translators | ||
=> This will result in two distinct source keys | ||
--> | ||
<span i18n="Verb|Text on a button used to report a problem">Report</span> | ||
<span i18n="Noun|Title of the Report section in the app">Report</span> | ||
// Bad use cases: | ||
<!-- Bad use cases: --> | ||
/* | ||
1. Using only descriptions, without meanings (note the missing pipe | ) | ||
<!-- Example 1 | ||
Using only descriptions, without meanings (note the missing pipe | ) | ||
-> This will result in only one source key | ||
*/ | ||
--> | ||
<label i18n="Label for the datepicker">Date</label> | ||
@@ -293,6 +350,6 @@ ... | ||
/* | ||
2. Using the same ID with two different source texts | ||
<!-- Example 2 | ||
Using the same ID with two different source texts | ||
-> This will result in only one source key (the first one) | ||
*/ | ||
--> | ||
<h2 i18n="@@section-title">First section</h2> | ||
@@ -309,3 +366,8 @@ <h2 i18n="@@section-title">Second section</h2> | ||
```html | ||
<span i18n>{catsCount, plural, =0 {There are no cats in the room} =1 {There is one cat in the room} other {There are {{catsCount}} cats in the room}}</span> | ||
<!-- Translators will see "There are no cats", "There is one cat", "There are {x} cats" --> | ||
<p i18n>{count, plural, | ||
=0 {There are no cats} | ||
=1 {There is one cat} | ||
other {There are {{count}} cats} | ||
}</p> | ||
``` | ||
@@ -312,0 +374,0 @@ |
@@ -92,2 +92,20 @@ const Interpolation = require('../utils/interpolation') | ||
forceArray(units) { | ||
let array = this.flattenArray([units]) | ||
return array.filter(unit => unit) // Remove null and undefined values | ||
} | ||
// Recursive function to flatten an array | ||
flattenArray(array, depth = 1, stack = []) { | ||
for (const unit of array) { | ||
if (Array.isArray(unit) && depth > 0) { | ||
this.flattenArray(unit, depth - 1, stack); | ||
} else { | ||
stack.push(unit); | ||
} | ||
} | ||
return stack; | ||
} | ||
xmlNodeText(xmlNode) { | ||
@@ -127,3 +145,3 @@ if (typeof xmlNode === 'string' || xmlNode instanceof String) { | ||
xmlUnitNotes(xmlUnit) { | ||
return [xmlUnit.note].flat().filter(unit => unit) // Ensure consistent array | ||
return this.forceArray(xmlUnit.note) // Ensure consistent array | ||
} | ||
@@ -133,3 +151,3 @@ | ||
xmlUnitContextGroups(xmlUnit) { | ||
return [xmlUnit['context-group']].flat().filter(unit => unit) // Ensure consistent array | ||
return this.forceArray(xmlUnit['context-group']) // Ensure consistent array | ||
} | ||
@@ -141,3 +159,5 @@ | ||
const contextNote = notes.find(note => note['@_from'] === 'meaning') | ||
const isCustomId = (id) => !/^\d+$/.test(id) // to separate generated IDs with manual IDs | ||
const isCustomId = (id) => !/^\d+$|^\w{40}$/.test(id) // to separate generated IDs with manual IDs | ||
// => old format: 7ad2c4ad8cd2978acd5e642c3825530e7ee7b7d7 | ||
// => new format: 392942015236586892 | ||
@@ -251,3 +271,3 @@ if (isCustomId(id)) { | ||
const targetXml = this.xmlParser().parse(targetRaw) | ||
const targetXmlUnits = [targetXml.xliff.file.body['trans-unit']].flat().filter(unit => unit) // Ensure consistent array | ||
const targetXmlUnits = this.forceArray(targetXml.xliff.file.body['trans-unit']) // Ensure consistent array | ||
@@ -254,0 +274,0 @@ // 4 Populate the loaded .xlf it with targets from Translation.io |
@@ -16,2 +16,4 @@ const Base = require('./base') | ||
let request = { | ||
client: 'angular', | ||
version: require('../../package.json').version, | ||
source_language: this.sourceLanguage(), | ||
@@ -25,3 +27,3 @@ target_languages: this.targetLanguages(), | ||
const sourceXml = this.xmlParser().parse(sourceRaw) | ||
const sourceXmlUnits = [sourceXml.xliff.file.body['trans-unit']].flat().filter(unit => unit) // Ensure consistent array | ||
const sourceXmlUnits = this.forceArray(sourceXml.xliff.file.body['trans-unit']) // Ensure consistent array | ||
const sourceSegments = this.convertXmlUnitsToSegments(sourceXmlUnits) | ||
@@ -39,3 +41,3 @@ | ||
const targetXml = this.xmlParser().parse(targetRaw) | ||
const targetXmlUnits = [targetXml.xliff.file.body['trans-unit']].flat().filter(unit => unit) // Ensure consistent array | ||
const targetXmlUnits = this.forceArray(targetXml.xliff.file.body['trans-unit']) // Ensure consistent array | ||
targetSegments = this.convertXmlUnitsToSegments(targetXmlUnits) | ||
@@ -67,3 +69,4 @@ } | ||
console.log(`Use this URL to translate: ${response.data.project.url}`) | ||
console.log("Then use 'npm run translation:sync' or 'yarn translation:sync' to send new keys to Translation.io and get new translations into your project.") | ||
console.log("Then use 'npm run translation:sync' or 'yarn translation:sync' ") | ||
console.log("to send new keys to Translation.io and get new translations into your project.") | ||
}, | ||
@@ -70,0 +73,0 @@ error => { |
@@ -20,3 +20,3 @@ const Base = require('./base') | ||
const sourceXml = this.xmlParser().parse(sourceRaw) | ||
const sourceXmlUnits = [sourceXml.xliff.file.body['trans-unit']].flat().filter(unit => unit) // Ensure consistent array | ||
const sourceXmlUnits = this.forceArray(sourceXml.xliff.file.body['trans-unit']) // Ensure consistent array | ||
const sourceSegments = this.convertXmlUnitsToSegments(sourceXmlUnits) | ||
@@ -30,2 +30,4 @@ | ||
let request = { | ||
client: 'angular', | ||
version: require('../../package.json').version, | ||
source_language: this.sourceLanguage(), | ||
@@ -32,0 +34,0 @@ target_languages: this.targetLanguages(), |
@@ -11,7 +11,11 @@ class HtmlTagExtraction { | ||
static isOpeningTag(extraction) { | ||
return extraction.includes('equiv-text="<') && extraction.includes('>"') && !this.isClosingTag(extraction) && !this.isSelfClosingTag(extraction) | ||
const hasEquivTextForOpening = extraction.includes('equiv-text="<') && extraction.includes('>"') | ||
const hasIdForOpening = extraction.includes('<x id="START_') | ||
return (hasEquivTextForOpening || hasIdForOpening) && !this.isClosingTag(extraction) && !this.isSelfClosingTag(extraction) | ||
} | ||
static isClosingTag(extraction) { | ||
return extraction.includes('equiv-text="</') && extraction.includes('>"') | ||
const hasEquivTextForClosing = extraction.includes('equiv-text="</') && extraction.includes('>"') | ||
const hasIdForClosing = extraction.includes('<x id="CLOSE_') | ||
return hasEquivTextForClosing || hasIdForClosing | ||
} | ||
@@ -21,3 +25,5 @@ | ||
const id = this.getId(extraction) | ||
return ['LINE_BREAK','HORIZONTAL_RULE'].includes(id) || (extraction.includes('equiv-text="<') && extraction.includes('/>"')) | ||
const hasEquivTextForSelfClosing = extraction.includes('equiv-text="<') && extraction.includes('/>"') | ||
const hasIdForSelfClosing = ['LINE_BREAK', 'HORIZONTAL_RULE', 'TAG_IMG'].includes(id) | ||
return hasEquivTextForSelfClosing || hasIdForSelfClosing | ||
} | ||
@@ -24,0 +30,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44435
605
435