]> BookStack Code Mirror - bookstack/blob - resources/js/services/translations.js
Added examples, updated docs for image gallery api endpoints
[bookstack] / resources / js / services / translations.js
1 /**
2  *  Translation Manager
3  *  Handles the JavaScript side of translating strings
4  *  in a way which fits with Laravel.
5  */
6 class Translator {
7
8     /**
9      * Create an instance, Passing in the required translations
10      * @param translations
11      */
12     constructor(translations) {
13         this.store = new Map();
14         this.parseTranslations();
15     }
16
17     /**
18      * Parse translations out of the page and place into the store.
19      */
20     parseTranslations() {
21         const translationMetaTags = document.querySelectorAll('meta[name="translation"]');
22         for (let tag of translationMetaTags) {
23             const key = tag.getAttribute('key');
24             const value = tag.getAttribute('value');
25             this.store.set(key, value);
26         }
27     }
28
29     /**
30      * Get a translation, Same format as laravel's 'trans' helper
31      * @param key
32      * @param replacements
33      * @returns {*}
34      */
35     get(key, replacements) {
36         const text = this.getTransText(key);
37         return this.performReplacements(text, replacements);
38     }
39
40     /**
41      * Get pluralised text, Dependant on the given count.
42      * Same format at laravel's 'trans_choice' helper.
43      * @param key
44      * @param count
45      * @param replacements
46      * @returns {*}
47      */
48     getPlural(key, count, replacements) {
49         const text = this.getTransText(key);
50         return this.parsePlural(text, count, replacements);
51     }
52
53     /**
54      * Parse the given translation and find the correct plural option
55      * to use. Similar format at laravel's 'trans_choice' helper.
56      * @param {String} translation
57      * @param {Number} count
58      * @param {Object} replacements
59      * @returns {String}
60      */
61     parsePlural(translation, count, replacements) {
62         const splitText = translation.split('|');
63         const exactCountRegex = /^{([0-9]+)}/;
64         const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
65         let result = null;
66
67         for (let t of splitText) {
68             // Parse exact matches
69             const exactMatches = t.match(exactCountRegex);
70             if (exactMatches !== null && Number(exactMatches[1]) === count) {
71                 result = t.replace(exactCountRegex, '').trim();
72                 break;
73             }
74
75             // Parse range matches
76             const rangeMatches = t.match(rangeRegex);
77             if (rangeMatches !== null) {
78                 const rangeStart = Number(rangeMatches[1]);
79                 if (rangeStart <= count && (rangeMatches[2] === '*' || Number(rangeMatches[2]) >= count)) {
80                     result = t.replace(rangeRegex, '').trim();
81                     break;
82                 }
83             }
84         }
85
86         if (result === null && splitText.length > 1) {
87             result = (count === 1) ? splitText[0] : splitText[1];
88         }
89
90         if (result === null) {
91             result = splitText[0];
92         }
93
94         return this.performReplacements(result, replacements);
95     }
96
97     /**
98      * Fetched translation text from the store for the given key.
99      * @param key
100      * @returns {String|Object}
101      */
102     getTransText(key) {
103         const value = this.store.get(key);
104
105         if (value === undefined) {
106             console.warn(`Translation with key "${key}" does not exist`);
107         }
108
109         return value;
110     }
111
112     /**
113      * Perform replacements on a string.
114      * @param {String} string
115      * @param {Object} replacements
116      * @returns {*}
117      */
118     performReplacements(string, replacements) {
119         if (!replacements) return string;
120         const replaceMatches = string.match(/:([\S]+)/g);
121         if (replaceMatches === null) return string;
122         replaceMatches.forEach(match => {
123             const key = match.substring(1);
124             if (typeof replacements[key] === 'undefined') return;
125             string = string.replace(match, replacements[key]);
126         });
127         return string;
128     }
129
130 }
131
132 export default Translator;