UNPKG

5.87 kBPlain TextView Raw
1import {AfterViewChecked, ChangeDetectorRef, Directive, ElementRef, Input, OnDestroy} from '@angular/core';
2import {Subscription, isObservable} from 'rxjs';
3import {DefaultLangChangeEvent, LangChangeEvent, TranslateService, TranslationChangeEvent} from './translate.service';
4import {equals, isDefined} from './util';
5
6@Directive({
7 selector: '[translate],[ngx-translate]'
8})
9export class TranslateDirective implements AfterViewChecked, OnDestroy {
10 key!: string;
11 lastParams: any;
12 currentParams: any;
13 onLangChangeSub!: Subscription;
14 onDefaultLangChangeSub!: Subscription;
15 onTranslationChangeSub!: Subscription;
16
17 @Input() set translate(key: string) {
18 if (key) {
19 this.key = key;
20 this.checkNodes();
21 }
22 }
23
24 @Input() set translateParams(params: any) {
25 if (!equals(this.currentParams, params)) {
26 this.currentParams = params;
27 this.checkNodes(true);
28 }
29 }
30
31 constructor(private translateService: TranslateService, private element: ElementRef, private _ref: ChangeDetectorRef) {
32 // subscribe to onTranslationChange event, in case the translations of the current lang change
33 if (!this.onTranslationChangeSub) {
34 this.onTranslationChangeSub = this.translateService.onTranslationChange.subscribe((event: TranslationChangeEvent) => {
35 if (event.lang === this.translateService.currentLang) {
36 this.checkNodes(true, event.translations);
37 }
38 });
39 }
40
41 // subscribe to onLangChange event, in case the language changes
42 if (!this.onLangChangeSub) {
43 this.onLangChangeSub = this.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
44 this.checkNodes(true, event.translations);
45 });
46 }
47
48 // subscribe to onDefaultLangChange event, in case the default language changes
49 if (!this.onDefaultLangChangeSub) {
50 this.onDefaultLangChangeSub = this.translateService.onDefaultLangChange.subscribe((event: DefaultLangChangeEvent) => {
51 this.checkNodes(true);
52 });
53 }
54 }
55
56 ngAfterViewChecked() {
57 this.checkNodes();
58 }
59
60 checkNodes(forceUpdate = false, translations?: any) {
61 let nodes: NodeList = this.element.nativeElement.childNodes;
62 // if the element is empty
63 if (!nodes.length) {
64 // we add the key as content
65 this.setContent(this.element.nativeElement, this.key);
66 nodes = this.element.nativeElement.childNodes;
67 }
68 for (let i = 0; i < nodes.length; ++i) {
69 let node: any = nodes[i];
70 if (node.nodeType === 3) { // node type 3 is a text node
71 let key!: string;
72 if (forceUpdate) {
73 node.lastKey = null;
74 }
75 if(isDefined(node.lookupKey)) {
76 key = node.lookupKey;
77 } else if (this.key) {
78 key = this.key;
79 } else {
80 let content = this.getContent(node);
81 let trimmedContent = content.trim();
82 if (trimmedContent.length) {
83 node.lookupKey = trimmedContent;
84 // we want to use the content as a key, not the translation value
85 if (content !== node.currentValue) {
86 key = trimmedContent;
87 // the content was changed from the user, we'll use it as a reference if needed
88 node.originalContent = content || node.originalContent;
89 } else if (node.originalContent) { // the content seems ok, but the lang has changed
90 // the current content is the translation, not the key, use the last real content as key
91 key = node.originalContent.trim();
92 } else if (content !== node.currentValue) {
93 // we want to use the content as a key, not the translation value
94 key = trimmedContent;
95 // the content was changed from the user, we'll use it as a reference if needed
96 node.originalContent = content || node.originalContent;
97 }
98 }
99 }
100 this.updateValue(key, node, translations);
101 }
102 }
103 }
104
105 updateValue(key: string, node: any, translations: any) {
106 if (key) {
107 if (node.lastKey === key && this.lastParams === this.currentParams) {
108 return;
109 }
110
111 this.lastParams = this.currentParams;
112
113 let onTranslation = (res: unknown) => {
114 if (res !== key) {
115 node.lastKey = key;
116 }
117 if (!node.originalContent) {
118 node.originalContent = this.getContent(node);
119 }
120 node.currentValue = isDefined(res) ? res : (node.originalContent || key);
121 // we replace in the original content to preserve spaces that we might have trimmed
122 this.setContent(node, this.key ? node.currentValue : node.originalContent.replace(key, node.currentValue));
123 this._ref.markForCheck();
124 };
125
126 if (isDefined(translations)) {
127 let res = this.translateService.getParsedResult(translations, key, this.currentParams);
128 if (isObservable(res)) {
129 res.subscribe({next: onTranslation});
130 } else {
131 onTranslation(res);
132 }
133 } else {
134 this.translateService.get(key, this.currentParams).subscribe(onTranslation);
135 }
136 }
137 }
138
139 getContent(node: any): string {
140 return isDefined(node.textContent) ? node.textContent : node.data;
141 }
142
143 setContent(node: any, content: string): void {
144 if (isDefined(node.textContent)) {
145 node.textContent = content;
146 } else {
147 node.data = content;
148 }
149 }
150
151 ngOnDestroy() {
152 if (this.onLangChangeSub) {
153 this.onLangChangeSub.unsubscribe();
154 }
155
156 if (this.onDefaultLangChangeSub) {
157 this.onDefaultLangChangeSub.unsubscribe();
158 }
159
160 if (this.onTranslationChangeSub) {
161 this.onTranslationChangeSub.unsubscribe();
162 }
163 }
164}