Skip to content

Commit

Permalink
Merge branch 'master' into NAS-130055-pt2
Browse files Browse the repository at this point in the history
  • Loading branch information
RehanY147 committed Aug 4, 2024
2 parents ce04938 + 27e07b9 commit a65555c
Show file tree
Hide file tree
Showing 172 changed files with 2,350 additions and 1,373 deletions.
50 changes: 0 additions & 50 deletions .github/workflows/main-release-engineering.yml

This file was deleted.

12 changes: 0 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,6 @@ jobs:
- name: Build
run: yarn lint

lint-translations:
name: Validate translation strings
needs: [install]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install
uses: ./.github/actions/prepare
- name: Validate
run: yarn run validate-translations

test:
name: Run tests
needs: [install]
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/validate-translations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: WebUI
on:
push:
branches:
- master
paths:
- 'src/assets/i18n/**'
pull_request:
branches:
- '**'
paths:
- 'src/assets/i18n/**'

jobs:
install:
name: Checkout and Install
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install
uses: ./.github/actions/prepare

lint-translations:
name: Validate translation strings
needs: [install]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install
uses: ./.github/actions/prepare
- name: Validate
run: yarn run validate-translations
10 changes: 1 addition & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,7 @@ ESLint is set up to ensure that the code is up to the format standards that we w

### Translating the UI

If you want to contribute to the translation of our UI, you can do so by editing one of the `[language_name].json` files under `src\assets\i18n\`. In each of those files, text strings and their translations in the relevant language are kept as key/value pairs e.g.,

`
{
...,
"Translate this text": "[Translation]",
...
}
`
See the [TRANSLATING.md](https://github.com/truenas/webui/blob/master/TRANSLATING.md) for more information on how to translate the UI.

## Tests

Expand Down
30 changes: 2 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,35 +104,9 @@ yarn install

This should bring the yarn environment back to a usable state.

# Translating Text to Other Languages
# Translating UI

All JSON files for translating TrueNAS's web interface are included in this repository under [src/assets/i18n](https://github.com/truenas/webui/tree/master/src/assets/i18n). \
These can be edited using your editor of choice or directly via the GitHub Web based code editing system.

Translation needs to be added to the right part of the string. For example:

```
"1 day": "1 jour",
```

Some strings may use [ICU Message Format](https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format) for pluralization.\
You can move tokens around and adjust them for your language.

For example:

```
Deleted {n, plural, one {# snapshot} other {# snapshots}}
```

can be translated in Russian as:

```
{n, plural, =1 {Снимок удален} few{# снимка удалено} other {# снимков удалено}}
```

You can test your strings in an [online editor](http://format-message.github.io/icu-message-format-for-translators/editor.html).

String files are often changed by developers and other contributors, so it's better to make multiple smaller PRs instead of trying to translate everything at once.
See the [TRANSLATING.md](https://github.com/truenas/webui/blob/master/TRANSLATING.md) for more information on how to translate the UI.

# Contributing

Expand Down
95 changes: 95 additions & 0 deletions TRANSLATING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Translating TrueNAS UI

All JSON files for translating TrueNAS web interface are included in this repository under [src/assets/i18n](https://github.com/truenas/webui/tree/master/src/assets/i18n). \
These can be edited using your editor of choice or directly via the GitHub Web based code editing system.

Translation needs to be added to the right part of the string. For example:

```
"1 day": "",
```

becomes

```
"1 day": "1 jour",
```

### Branches

Different branches correspond to different versions of TrueNAS.

It's easiest to make changes to the `master` branch, which corresponds to the most recent unreleased version of TrueNAS SCALE.

Once changes to `master` are merged, they will appear in next nightly build.

### General Recommendations

- Source files may often change, so it's better to make multiple smaller PRs instead of trying to translate everything at once.
- CI job will validate your changes and will fail if there are any issues.
- If you want to validate translation strings locally, you need to have Node.js and `yarn` installed, do `yarn install` and execute `yarn validate-translations` in the root of the project.

### Placeholder Tokens

Some strings may contain placeholder tokens in `{curly braces}`.

For example:

```
"Delete {file}?": "",
```

In the UI `{file}` will be replaced with the name of the file, resulting in a string like `"Delete file.txt?"`.

You should keep these tokens as is in the translated string, but you can move them around if needed.

```
"Delete {file}?": "Supprimer {file}?",
```

### Plural Forms

Some strings may use [ICU Message Format](https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format) for pluralization.

For example,

```
{n, plural, one {User} other {# users}} deleted
```

may show either `User deleted` or `5 users deleted` depending on the value of `n`.

Everything inside the curly braces is a part of the ICU Message Format.

This particular example can be read as:

1. Look at the value of `n`.
2. If `n` is `1`, print `User`.
3. If `n` is anything else, print `# users`, where `#` is replaced with the value of `n`.
4. Add `deleted` at the end.

#### Translating Plural Forms

Different languages have different ways of expressing plural forms.

In English plurality is expressed by changing the form of the noun: `User -> users`.

In Spanish, both noun and verb may change: `Usuario eliminado -> Usuarios eliminados`.

You can express it in the following way:

```
{n, plural, one {Usuario eliminado} other {# usuarios eliminados}}
```

Russian is an example of a language that has more than just singular and plural form of the word. It has one word for 1 item, another for 2 to 4 items and yet another for 5 items and more: `Пользователь -> пользователя -> пользователей`.

This can be expressed via:

```
{n, plural, =1 {Пользователь удален} few{# пользователя удалено} other {# пользователей удалено}}
```

You would have to research ICU Message Format for your language to find out how to express plural forms.

Plural strings can be tested in an [online editor](http://format-message.github.io/icu-message-format-for-translators/editor.html).
8 changes: 6 additions & 2 deletions scripts/validate_translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ fs.readdir(translationDir, function (err, files) {
parse(translation);
} catch (error) {
hadErrors = true;
console.error("Error parsing translation string. You may need to escape { } to '{' '}'. Offending string:");
console.error(`${language}: "${key}"`, JSON.stringify(error));

if (error.location) {
console.error(`${language}.json, line ${error.location.start.line}: ${error.message}`);
} else {
console.error(`${language}.json: ${error.message}`);
}
}
});
});
Expand Down
83 changes: 10 additions & 73 deletions src/app/directives/ui-search.directive.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {
Directive, Input, ElementRef, Renderer2, OnInit,
OnDestroy,
Inject,
} from '@angular/core';
import { WINDOW } from 'app/helpers/window.helper';
import { searchDelayConst } from 'app/modules/global-search/constants/delay.const';
import { getSearchableElementId } from 'app/modules/global-search/helpers/get-searchable-element-id';
import { UiSearchableElement } from 'app/modules/global-search/interfaces/ui-searchable-element.interface';
Expand Down Expand Up @@ -37,7 +35,6 @@ export class UiSearchDirective implements OnInit, OnDestroy {
private renderer: Renderer2,
private elementRef: ElementRef<HTMLElement>,
private searchDirectives: UiSearchDirectivesService,
@Inject(WINDOW) private window: Window,
) {}

ngOnInit(): void {
Expand Down Expand Up @@ -81,85 +78,25 @@ export class UiSearchDirective implements OnInit, OnDestroy {
private highlightAndClickElement(anchorRef: HTMLElement, shouldClick = false): void {
if (!anchorRef) return;

const arrowPointer = this.createArrowPointer();
this.renderer.addClass(anchorRef, 'search-element-highlighted');

if (shouldClick) setTimeout(() => anchorRef.click(), searchDelayConst);

setTimeout(() => {
anchorRef.focus();
anchorRef.scrollIntoView();
document.querySelector<HTMLElement>('.rightside-content-hold')?.scrollBy(0, -20);

if (!shouldClick) this.positionArrowPointer(anchorRef, arrowPointer);
document.querySelector<HTMLElement>('.rightside-content-hold').scrollBy(0, -20);
}, searchDelayConst);

setTimeout(() => this.removeArrowPointer(arrowPointer), searchDelayConst * 15);
const handleEvent = (): void => {
this.renderer.removeClass(anchorRef, 'search-element-highlighted');
document.removeEventListener('click', handleEvent);
document.removeEventListener('keydown', handleEvent);
};

setTimeout(() => {
['click', 'keydown'].forEach((event) => {
this.window.addEventListener(event, () => this.removeArrowPointer(arrowPointer), { once: true });
});
});
}

private positionArrowPointer(anchorRef: HTMLElement, arrowElement: HTMLElement): void {
const rect = anchorRef.getBoundingClientRect();
const viewportWidth = this.window.innerWidth;
const arrowMaxWidth = 90;
const arrowRightOffset = 80;
const arrowLeftOffset = 10;
const arrowTopOffset = 25;

if (rect.top === 0) {
return;
}

const topPosition = `${this.window.scrollY + rect.top + rect.height / 2 - arrowTopOffset}px`;
let leftPosition = `${this.window.scrollX + rect.right - arrowLeftOffset}px`;

if (rect.right + arrowMaxWidth > viewportWidth) {
leftPosition = `${this.window.scrollX + rect.left - arrowRightOffset}px`;
this.renderer.addClass(arrowElement, 'arrow-left');
} else {
this.renderer.addClass(arrowElement, 'arrow-right');
}

this.renderer.setStyle(arrowElement, 'top', topPosition);
this.renderer.setStyle(arrowElement, 'left', leftPosition);
this.renderer.setStyle(arrowElement, 'opacity', '1');

this.renderer.appendChild(this.window.document.body, arrowElement);
}

private removeArrowPointer(arrowElement: HTMLElement): void {
this.renderer.setStyle(arrowElement, 'opacity', '0');
setTimeout(() => {
if (arrowElement.parentNode) {
arrowElement.parentNode.removeChild(arrowElement);
}
}, 300);
}

private createArrowPointer(): HTMLElement {
const svgElement = this.renderer.createElement('svg', 'http://www.w3.org/2000/svg') as HTMLElement;
this.renderer.setAttribute(svgElement, 'width', '90');
this.renderer.setAttribute(svgElement, 'height', '50');
this.renderer.setAttribute(svgElement, 'viewBox', '0 0 700 700');

const gElement = this.renderer.createElement('g', 'http://www.w3.org/2000/svg') as HTMLElement;
this.renderer.setAttribute(gElement, 'transform', 'matrix(0,-1,1,0,28.57143,680.00001)');

const pathElement = this.renderer.createElement('path', 'http://www.w3.org/2000/svg') as HTMLElement;
this.renderer.setAttribute(pathElement, 'd', 'M 680.00001,-27.858649 C 680.00001,-26.000219 330.00773,672.12987 329.42789,671.42805 C 327.4824,669.07328 -20.639627,-28.032809 -19.999107,-28.291219 C -19.580737,-28.459999 59.298143,5.3997002 155.28729,46.952552 L 329.81303,122.50321 L 504.05513,46.965887 C 599.8883,5.4203742 678.68037,-28.571419 679.14863,-28.571419 C 679.61689,-28.571419 680.00001,-28.250669 680.00001,-27.858649 z');
this.renderer.setAttribute(pathElement, 'fill', 'var(--primary)');
this.renderer.setAttribute(pathElement, 'stroke', 'var(--fg1)');
this.renderer.setAttribute(pathElement, 'stroke-width', '40');

this.renderer.appendChild(gElement, pathElement);
this.renderer.appendChild(svgElement, gElement);

this.renderer.addClass(svgElement, 'arrow-element');

return svgElement;
document.addEventListener('click', handleEvent, { once: true });
document.addEventListener('keydown', handleEvent, { once: true });
}, 0);
}
}
3 changes: 3 additions & 0 deletions src/app/helptext/system/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ export const helptextSystemGeneral = {
stg_language: {
placeholder: T('Language'),
tooltip: T('Select a language from the drop-down menu.'),
hint: T('Languages other than <i>English</i> are provided by \
the community and may be incomplete. \
<a href="https://github.com/truenas/webui/blob/master/TRANSLATING.md" target="_blank">Learn how to contribute.</a>'),
},

stg_kbdmap: {
Expand Down
Loading

0 comments on commit a65555c

Please sign in to comment.