@@ -3,6 +3,98 @@ import { VNode } from 'vue/types/vnode'
3
3
import { warn } from '../util/warn'
4
4
import { FluentVueObject } from '../types'
5
5
6
+ // This part is from fluent-dom library
7
+ const LOCALIZABLE_ATTRIBUTES = {
8
+ 'http://www.w3.org/1999/xhtml' : {
9
+ global : [ 'title' , 'aria-label' , 'aria-valuetext' , 'aria-moz-hint' ] ,
10
+ a : [ 'download' ] ,
11
+ area : [ 'download' , 'alt' ] ,
12
+ // value is special-cased in isAttrNameLocalizable
13
+ input : [ 'alt' , 'placeholder' ] ,
14
+ menuitem : [ 'label' ] ,
15
+ menu : [ 'label' ] ,
16
+ optgroup : [ 'label' ] ,
17
+ option : [ 'label' ] ,
18
+ track : [ 'label' ] ,
19
+ img : [ 'alt' ] ,
20
+ textarea : [ 'placeholder' ] ,
21
+ th : [ 'abbr' ]
22
+ } ,
23
+ 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' : {
24
+ global : [ 'accesskey' , 'aria-label' , 'aria-valuetext' , 'aria-moz-hint' , 'label' ] ,
25
+ key : [ 'key' , 'keycode' ] ,
26
+ textbox : [ 'placeholder' ] ,
27
+ toolbarbutton : [ 'tooltiptext' ]
28
+ }
29
+ } as any
30
+
31
+ /**
32
+ * Check if attribute is allowed for the given element.
33
+ *
34
+ * This method is used by the sanitizer when the translation markup contains
35
+ * DOM attributes, or when the translation has traits which map to DOM
36
+ * attributes.
37
+ *
38
+ * `explicitlyAllowed` can be passed as a list of attributes explicitly
39
+ * allowed on this element.
40
+ *
41
+ * @param {string } name
42
+ * @param {Element } element
43
+ * @param {Array } explicitlyAllowed
44
+ * @returns {boolean }
45
+ * @private
46
+ */
47
+ function isAttrNameLocalizable (
48
+ name : string ,
49
+ element : HTMLElement ,
50
+ explicitlyAllowed : Array < string > | null = null
51
+ ) {
52
+ if ( explicitlyAllowed && explicitlyAllowed . includes ( name ) ) {
53
+ return true
54
+ }
55
+
56
+ if ( element . namespaceURI === null ) {
57
+ return false
58
+ }
59
+
60
+ const allowed = LOCALIZABLE_ATTRIBUTES [ element . namespaceURI ]
61
+ if ( ! allowed ) {
62
+ return false
63
+ }
64
+
65
+ const attrName = name . toLowerCase ( )
66
+ const elemName = element . localName
67
+
68
+ // Is it a globally safe attribute?
69
+ if ( allowed . global . includes ( attrName ) ) {
70
+ return true
71
+ }
72
+
73
+ // Are there no allowed attributes for this element?
74
+ if ( ! allowed [ elemName ] ) {
75
+ return false
76
+ }
77
+
78
+ // Is it allowed on this element?
79
+ if ( allowed [ elemName ] . includes ( attrName ) ) {
80
+ return true
81
+ }
82
+
83
+ // Special case for value on HTML inputs with type button, reset, submit
84
+ if (
85
+ element . namespaceURI === 'http://www.w3.org/1999/xhtml' &&
86
+ elemName === 'input' &&
87
+ attrName === 'value'
88
+ ) {
89
+ const type = ( element as HTMLInputElement ) . type . toLowerCase ( )
90
+ if ( type === 'submit' || type === 'button' || type === 'reset' ) {
91
+ return true
92
+ }
93
+ }
94
+
95
+ return false
96
+ }
97
+
6
98
function translate ( el : HTMLElement , fluent : FluentVueObject , binding : DirectiveBinding ) {
7
99
const key = binding . arg
8
100
@@ -22,8 +114,11 @@ function translate(el: HTMLElement, fluent: FluentVueObject, binding: DirectiveB
22
114
el . textContent = fluent . formatPattern ( bundle , msg . value , binding . value )
23
115
}
24
116
25
- for ( const [ attr ] of Object . entries ( binding . modifiers ) ) {
26
- el . setAttribute ( attr , fluent . formatPattern ( bundle , msg . attributes [ attr ] , binding . value ) )
117
+ const allowedAttrs = Object . keys ( binding . modifiers )
118
+ for ( const [ attr , attrValue ] of Object . entries ( msg . attributes ) ) {
119
+ if ( isAttrNameLocalizable ( attr , el , allowedAttrs ) ) {
120
+ el . setAttribute ( attr , fluent . formatPattern ( bundle , attrValue , binding . value ) )
121
+ }
27
122
}
28
123
}
29
124
0 commit comments