Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Fetch and return language strings.
18
 *
19
 * @module     core/str
20
 * @copyright  2015 Damyon Wiese <damyon@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 * @since      2.9
23
 *
24
 */
25
import $ from 'jquery';
26
import Ajax from 'core/ajax';
27
import Config from 'core/config';
28
import LocalStorage from 'core/localstorage';
29
 
30
/**
31
 * @typedef StringRequest
32
 * @type {object}
33
 * @param {string} requests.key The string identifer to fetch
34
 * @param {string} [requests.component='core'] The componet to fetch from
35
 * @param {string} [requests.lang] The language to fetch a string for. Defaults to current page language.
36
 * @param {object|string} [requests.param] The param for variable expansion in the string.
37
 */
38
 
39
// Module cache for the promises so that we don't make multiple
40
// unnecessary requests.
41
let promiseCache = [];
42
 
43
/* eslint-disable no-restricted-properties */
44
 
45
/**
46
 * Return a Promise that resolves to a string.
47
 *
48
 * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched
49
 * from the server and resolved when available.
50
 *
51
 * @param {string} key The language string key
52
 * @param {string} [component='core'] The language string component
53
 * @param {object|string} [param] The param for variable expansion in the string.
54
 * @param {string} [lang] The users language - if not passed it is deduced.
55
 * @return {jQuery.Promise} A jQuery Promise containing the translated string
56
 *
57
 * @example <caption>Fetching a string</caption>
58
 *
59
 * import {getString} from 'core/str';
60
 * get_string('cannotfindteacher', 'error')
61
 * .then((str) => window.console.log(str)); // Cannot find teacher
62
 */
63
// eslint-disable-next-line camelcase
64
export const get_string = (key, component, param, lang) => {
65
    return get_strings([{key, component, param, lang}])
66
        .then(results => results[0]);
67
};
68
 
69
/**
70
 * Return a Promise that resolves to a string.
71
 *
72
 * If the string has previously been cached, then the Promise will be resolved immediately, otherwise it will be fetched
73
 * from the server and resolved when available.
74
 *
75
 * @param {string} key The language string key
76
 * @param {string} [component='core'] The language string component
77
 * @param {object|string} [param] The param for variable expansion in the string.
78
 * @param {string} [lang] The users language - if not passed it is deduced.
79
 * @return {Promise<string>} A native Promise containing the translated string
80
 *
81
 * @example <caption>Fetching a string</caption>
82
 *
83
 * import {getString} from 'core/str';
84
 *
85
 * getString('cannotfindteacher', 'error')
86
 * .then((str) => window.console.log(str)); // Cannot find teacher
87
 */
88
export const getString = (key, component, param, lang) =>
89
    getRequestedStrings([{key, component, param, lang}])[0];
90
 
91
/**
92
 * Make a batch request to load a set of strings.
93
 *
94
 * Any missing string will be fetched from the server.
95
 * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.
96
 *
97
 * @param {Array.<StringRequest>} requests List of strings to fetch
98
 * @return {Promise<string[]>} A native promise containing an array of the translated strings
99
 *
100
 * @example <caption>Fetching a set of strings</caption>
101
 *
102
 * import {getStrings} from 'core/str';
103
 * getStrings([
104
 *     {
105
 *         key: 'cannotfindteacher',
106
 *         component: 'error',
107
 *     },
108
 *     {
109
 *         key: 'yes',
110
 *         component: 'core',
111
 *     },
112
 *     {
113
 *         key: 'no',
114
 *         component: 'core',
115
 *     },
116
 * ])
117
 * .then((cannotFindTeacher, yes, no) => {
118
 *     window.console.log(cannotFindTeacher); // Cannot find teacher
119
 *     window.console.log(yes); // Yes
120
 *     window.console.log(no); // No
121
 * });
122
 */
123
export const getStrings = (requests) => Promise.all(getRequestedStrings(requests));
124
 
125
/**
126
 * Internal function to perform the string requests.
127
 *
128
 * @param {Array.<StringRequest>} requests List of strings to fetch
129
 * @returns {Promise[]}
130
 */
131
const getRequestedStrings = (requests) => {
132
    let requestData = [];
133
    const pageLang = Config.language;
134
 
135
    // Helper function to construct the cache key.
136
    const getCacheKey = ({key, component, lang = pageLang}) => `core_str/${key}/${component}/${lang}`;
137
 
138
    const stringPromises = requests.map((request) => {
139
        let {component, key, param, lang = pageLang} = request;
140
        if (!component) {
141
            component = 'core';
142
        }
143
 
144
        const cacheKey = getCacheKey({key, component, lang});
145
 
146
        // Helper function to add the promise to cache.
147
        const buildReturn = (promise) => {
148
            // Make sure the promise cache contains our promise.
149
            promiseCache[cacheKey] = promise;
150
            return promise;
151
        };
152
 
153
        // Check if we can serve the string straight from M.str.
154
        if (component in M.str && key in M.str[component]) {
155
            return buildReturn(new Promise((resolve) => {
11 efrain 156
                resolve(M.util.get_string(key, component, param));
1 efrain 157
            }));
158
        }
159
 
160
        // Check if the string is in the browser's local storage.
161
        const cached = LocalStorage.get(cacheKey);
162
        if (cached) {
163
            M.str[component] = {...M.str[component], [key]: cached};
164
            return buildReturn(new Promise((resolve) => {
11 efrain 165
                resolve(M.util.get_string(key, component, param));
1 efrain 166
            }));
167
        }
168
 
169
        // Check if we've already loaded this string from the server.
170
        if (cacheKey in promiseCache) {
171
            return buildReturn(promiseCache[cacheKey]).then(() => {
11 efrain 172
                return M.util.get_string(key, component, param);
1 efrain 173
            });
174
        } else {
175
            // We're going to have to ask the server for the string so
176
            // add this string to the list of requests to be sent.
177
            return buildReturn(new Promise((resolve, reject) => {
178
                requestData.push({
179
                    methodname: 'core_get_string',
180
                    args: {
181
                        stringid: key,
182
                        stringparams: [],
183
                        component,
184
                        lang,
185
                    },
186
                    done: (str) => {
187
                        // When we get the response from the server
188
                        // we should update M.str and the browser's
189
                        // local storage before resolving this promise.
190
                        M.str[component] = {...M.str[component], [key]: str};
191
                        LocalStorage.set(cacheKey, str);
11 efrain 192
                        resolve(M.util.get_string(key, component, param));
1 efrain 193
                    },
194
                    fail: reject
195
                });
196
            }));
197
        }
198
    });
199
 
200
    if (requestData.length) {
201
        // If we need to load any strings from the server then send
202
        // off the request.
203
        Ajax.call(requestData, true, false, false, 0, M.cfg.langrev);
204
    }
205
 
206
    return stringPromises;
207
};
208
 
209
/**
210
 * Make a batch request to load a set of strings.
211
 *
212
 * Any missing string will be fetched from the server.
213
 * The Promise will only be resolved once all strings are available, or an attempt has been made to fetch them.
214
 *
215
 * @param {Array.<StringRequest>} requests List of strings to fetch
216
 * @return {jquery.Promise<string[]>} A jquery promise containing an array of the translated strings
217
 *
218
 * @example <caption>Fetching a set of strings</caption>
219
 *
220
 * import {getStrings} from 'core/str';
221
 * get_strings([
222
 *     {
223
 *         key: 'cannotfindteacher',
224
 *         component: 'error',
225
 *     },
226
 *     {
227
 *         key: 'yes',
228
 *         component: 'core',
229
 *     },
230
 *     {
231
 *         key: 'no',
232
 *         component: 'core',
233
 *     },
234
 * ])
235
 * .then((cannotFindTeacher, yes, no) => {
236
 *     window.console.log(cannotFindTeacher); // Cannot find teacher
237
 *     window.console.log(yes); // Yes
238
 *     window.console.log(no); // No
239
 * });
240
 */
241
// eslint-disable-next-line camelcase
242
export const get_strings = (requests) => {
243
    // We need to use jQuery here because some calling code uses the
244
    // .done handler instead of the .then handler.
245
    return $.when.apply($, getRequestedStrings(requests))
246
        .then((...strings) => strings);
247
};
248
 
249
/**
250
 * Add a list of strings to the caches.
251
 *
252
 * This function should typically only be called from core APIs to pre-cache values.
253
 *
254
 * @method cache_strings
255
 * @protected
256
 * @param {Object[]} strings List of strings to fetch
257
 * @param {string} strings.key The string identifer to fetch
258
 * @param {string} strings.value The string value
259
 * @param {string} [strings.component='core'] The componet to fetch from
260
 * @param {string} [strings.lang=Config.language] The language to fetch a string for. Defaults to current page language.
261
 */
262
// eslint-disable-next-line camelcase
263
export const cache_strings = (strings) => {
264
    strings.forEach(({key, component, value, lang = Config.language}) => {
265
        const cacheKey = ['core_str', key, component, lang].join('/');
266
 
267
        // Check M.str caching.
268
        if (!(component in M.str) || !(key in M.str[component])) {
269
            if (!(component in M.str)) {
270
                M.str[component] = {};
271
            }
272
 
273
            M.str[component][key] = value;
274
        }
275
 
276
        // Check local storage.
277
        if (!LocalStorage.get(cacheKey)) {
278
            LocalStorage.set(cacheKey, value);
279
        }
280
 
281
        // Check the promises cache.
282
        if (!(cacheKey in promiseCache)) {
283
            promiseCache[cacheKey] = $.Deferred().resolve(value).promise();
284
        }
285
    });
286
};
287
/* eslint-enable no-restricted-properties */