diff --git a/src/addon/mod/feedback/components/index/index.html b/src/addon/mod/feedback/components/index/index.html
index ccefec125..4f202fd92 100644
--- a/src/addon/mod/feedback/components/index/index.html
+++ b/src/addon/mod/feedback/components/index/index.html
@@ -47,7 +47,7 @@
{{ 'core.groupsseparate' | translate }}
{{ 'core.groupsvisible' | translate }}
-
+
{{groupOpt.name}}
diff --git a/src/addon/mod/feedback/pages/form/form.html b/src/addon/mod/feedback/pages/form/form.html
index 722813335..c67fc5376 100644
--- a/src/addon/mod/feedback/pages/form/form.html
+++ b/src/addon/mod/feedback/pages/form/form.html
@@ -49,7 +49,7 @@
-
+
diff --git a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html
index 906dfbd6b..180f3339a 100644
--- a/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html
+++ b/src/addon/mod/feedback/pages/nonrespondents/nonrespondents.html
@@ -12,7 +12,7 @@
{{ 'core.groupsseparate' | translate }}
{{ 'core.groupsvisible' | translate }}
-
+
{{groupOpt.name}}
diff --git a/src/addon/mod/feedback/pages/respondents/respondents.html b/src/addon/mod/feedback/pages/respondents/respondents.html
index f9e2c2dd5..1754bde05 100644
--- a/src/addon/mod/feedback/pages/respondents/respondents.html
+++ b/src/addon/mod/feedback/pages/respondents/respondents.html
@@ -13,7 +13,7 @@
{{ 'core.groupsseparate' | translate }}
{{ 'core.groupsvisible' | translate }}
-
+
{{groupOpt.name}}
diff --git a/src/addon/userprofilefield/menu/component/menu.html b/src/addon/userprofilefield/menu/component/menu.html
index 68198ddc9..39f54dbc8 100644
--- a/src/addon/userprofilefield/menu/component/menu.html
+++ b/src/addon/userprofilefield/menu/component/menu.html
@@ -6,7 +6,7 @@
{{ field.name }}
-
+
{{ 'core.choosedots' | translate }}
{{option}}
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index e3ba3417f..22f8c0aec 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -18,7 +18,9 @@ import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { CoreAppProvider } from '@providers/app';
import { CoreEventsProvider } from '@providers/events';
+import { CoreLangProvider } from '@providers/lang';
import { CoreLoggerProvider } from '@providers/logger';
+import { CoreSitesProvider } from '@providers/sites';
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
@Component({
@@ -33,7 +35,7 @@ export class MoodleMobileApp implements OnInit {
constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, logger: CoreLoggerProvider,
private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider,
- private appProvider: CoreAppProvider) {
+ private appProvider: CoreAppProvider, private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) {
this.logger = logger.getInstance('AppComponent');
platform.ready().then(() => {
@@ -49,9 +51,12 @@ export class MoodleMobileApp implements OnInit {
* Component being initialized.
*/
ngOnInit(): void {
- // Go to sites page when user is logged out.
this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
+ // Go to sites page when user is logged out.
this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage');
+
+ // Unload lang custom strings.
+ this.langProvider.clearCustomStrings();
});
// Listen for session expired events.
@@ -111,5 +116,25 @@ export class MoodleMobileApp implements OnInit {
this.eventsProvider.on(CoreEventsProvider.APP_LAUNCHED_URL, (url) => {
this.loginHelper.appLaunchedByURL(url);
});
+
+ // Load custom lang strings. This cannot be done inside the lang provider because it causes circular dependencies.
+ const loadCustomStrings = (): void => {
+ const currentSite = this.sitesProvider.getCurrentSite(),
+ customStrings = currentSite && currentSite.getStoredConfig('tool_mobile_customlangstrings');
+
+ if (typeof customStrings != 'undefined') {
+ this.langProvider.loadCustomStrings(customStrings);
+ }
+ };
+
+ this.eventsProvider.on(CoreEventsProvider.LOGIN, () => {
+ loadCustomStrings();
+ });
+
+ this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (siteId) => {
+ if (siteId == this.sitesProvider.getCurrentSiteId()) {
+ loadCustomStrings();
+ }
+ });
}
}
diff --git a/src/core/settings/pages/general/general.html b/src/core/settings/pages/general/general.html
index ea82aa063..d1be96153 100644
--- a/src/core/settings/pages/general/general.html
+++ b/src/core/settings/pages/general/general.html
@@ -6,7 +6,7 @@
{{ 'core.settings.language' | translate }}
-
+
{{ languages[code] }}
diff --git a/src/providers/lang.ts b/src/providers/lang.ts
index 6207168de..038001174 100644
--- a/src/providers/lang.ts
+++ b/src/providers/lang.ts
@@ -53,13 +53,10 @@ export class CoreLangProvider {
* @param {string} [prefix] A prefix to add to all keys.
*/
addSitePluginsStrings(lang: string, strings: any, prefix?: string): void {
- // Initialize structures if they don't exist.
+ // Initialize structure if it doesn't exist.
if (!this.sitePluginsStrings[lang]) {
this.sitePluginsStrings[lang] = {};
}
- if (!this.translate.translations[lang]) {
- this.translate.translations[lang] = {};
- }
for (const key in strings) {
const prefixedKey = prefix + key;
@@ -75,19 +72,8 @@ export class CoreLangProvider {
// Make sure we didn't add to many brackets in some case.
value = value.replace(/{{{([^ ]+)}}}/gm, '{{$1}}');
- if (!this.sitePluginsStrings[lang][prefixedKey]) {
- // It's a new site plugin string. Store the original value.
- this.sitePluginsStrings[lang][prefixedKey] = {
- original: this.translate.translations[lang][prefixedKey],
- value: value
- };
- } else {
- // Site plugin string already defined. Store the new value.
- this.sitePluginsStrings[lang][prefixedKey].value = value;
- }
-
- // Store the string in the translations table.
- this.translate.translations[lang][prefixedKey] = value;
+ // Load the string.
+ this.loadString(this.sitePluginsStrings, lang, prefixedKey, value);
}
}
@@ -100,13 +86,38 @@ export class CoreLangProvider {
changeCurrentLanguage(language: string): Promise {
const promises = [];
- promises.push(this.translate.use(language));
+ // Change the language, resolving the promise when we receive the first value.
+ promises.push(new Promise((resolve, reject): void => {
+ const subscription = this.translate.use(language).subscribe((data) => {
+ resolve(data);
+
+ // Data received, unsubscribe. Use a timeout because we can receive a value immediately.
+ setTimeout(() => {
+ subscription.unsubscribe();
+ });
+ }, (error) => {
+ reject(error);
+
+ // Error received, unsubscribe. Use a timeout because we can receive a value immediately.
+ setTimeout(() => {
+ subscription.unsubscribe();
+ });
+ });
+ }));
+
+ // Change the config.
promises.push(this.configProvider.set('current_language', language));
moment.locale(language);
this.currentLanguage = language;
- return Promise.all(promises);
+ return Promise.all(promises).finally(() => {
+ // Load the custom and site plugins strings for the language.
+ if (this.loadLangStrings(this.customStrings, language) || this.loadLangStrings(this.sitePluginsStrings, language)) {
+ // Some lang strings have changed, emit an event to update the pipes.
+ this.translate.onLangChange.emit({lang: language, translations: this.translate.translations[language]});
+ }
+ });
}
/**
@@ -227,15 +238,75 @@ export class CoreLangProvider {
this.customStrings[lang] = {};
}
- // Store the original value of the custom string.
- this.customStrings[lang][values[0]] = {
- original: this.translate.translations[lang][values[0]],
- value: values[1]
+ // Convert old keys format to new one.
+ const key = values[0].replace(/^mm\.core/, 'core').replace(/^mm\./, 'core.').replace(/^mma\./, 'addon.')
+ .replace(/^core\.sidemenu/, 'core.mainmenu').replace(/^addon\.grades/, 'core.grades')
+ .replace(/^addon\.participants/, 'core.user');
+
+ this.loadString(this.customStrings, lang, key, values[1]);
+ });
+
+ this.customStringsRaw = strings;
+ }
+
+ /**
+ * Load custom strings for a certain language that weren't loaded because the language wasn't active.
+ *
+ * @param {any} langObject The object with the strings to load.
+ * @param {string} lang Language to load.
+ * @return {boolean} Whether the translation table was modified.
+ */
+ loadLangStrings(langObject: any, lang: string): boolean {
+ let langApplied = false;
+
+ if (langObject[lang]) {
+ for (const key in langObject[lang]) {
+ const entry = langObject[lang][key];
+
+ if (!entry.applied) {
+ // Store the original value of the string.
+ entry.original = this.translate.translations[lang][key];
+
+ // Store the string in the translations table.
+ this.translate.translations[lang][key] = entry.value;
+
+ entry.applied = true;
+ langApplied = true;
+ }
+ }
+ }
+
+ return langApplied;
+ }
+
+ /**
+ * Load a string in a certain lang object and in the translate table if the lang is loaded.
+ *
+ * @param {any} langObject The object where to store the lang.
+ * @param {string} lang Language code.
+ * @param {string} key String key.
+ * @param {string} value String value.
+ */
+ loadString(langObject: any, lang: string, key: string, value: string): void {
+ if (this.translate.translations[lang]) {
+ // The language is loaded.
+ // Store the original value of the string.
+ langObject[lang][key] = {
+ original: this.translate.translations[lang][key],
+ value: value,
+ applied: true
};
// Store the string in the translations table.
- this.translate.translations[lang][values[0]] = values[1];
- });
+ this.translate.translations[lang][key] = value;
+ } else {
+ // The language isn't loaded.
+ // Save it in our object but not in the translations table, it will be loaded when the lang is loaded.
+ langObject[lang][key] = {
+ value: value,
+ applied: false
+ };
+ }
}
/**
@@ -246,6 +317,11 @@ export class CoreLangProvider {
protected unloadStrings(strings: any): void {
// Iterate over all languages and strings.
for (const lang in strings) {
+ if (!this.translate.translations[lang]) {
+ // Language isn't loaded, nothing to unload.
+ continue;
+ }
+
const langStrings = strings[lang];
for (const key in langStrings) {
const entry = langStrings[key];