Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make translations load synchronously #4267

Merged
merged 4 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions scripts/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ if (appConfig.features.autorefreshContent == null) {
export const dashboardRoute = '/workspace';
export const IDENTITY_KEY = 'sess:user';

// We have the same function in `scripts/translations.js`
// defined because there we can't import this one
// FIXME: When changing one or the other
// update both functions so they are the same
export function getUserInterfaceLanguage() {
thecalcc marked this conversation as resolved.
Show resolved Hide resolved
const user: IUser | null = JSON.parse(localStorage.getItem(IDENTITY_KEY));
const language = user?.language ?? appConfig.default_language ?? window.navigator.language ?? 'en';
Expand All @@ -41,8 +45,4 @@ export function getUserInterfaceLanguage() {
}
}

export const debugInfo = {
translationsLoaded: false,
};

export const extensions: IExtensions = {};
18 changes: 7 additions & 11 deletions scripts/core/services/translate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'angular-dynamic-locale';
import moment from 'moment';
import {gettext} from 'core/utils';
import {loadTranslations} from 'index';

/**
* Translate module
Expand All @@ -21,18 +20,15 @@ export default angular.module('superdesk.core.translate', [

.run(['gettextCatalog', '$location', '$rootScope', 'SESSION_EVENTS', 'tmhDynamicLocale',
function(gettextCatalog, $location, $rootScope, SESSION_EVENTS, tmhDynamicLocale) {
$rootScope.$on(SESSION_EVENTS.IDENTITY_LOADED, (event) => {
loadTranslations()
.then((res) => {
const {translations, language} = res;
$rootScope.$on(SESSION_EVENTS.IDENTITY_LOADED, () => {
const {translations, language} = window;

gettextCatalog.setCurrentLanguage(language);
gettextCatalog.setStrings(language, translations);
moment.locale(language); // set locale for date/time management
gettextCatalog.setCurrentLanguage(language);
gettextCatalog.setStrings(language, translations);
moment.locale(language); // set locale for date/time management

// set locale for angular-i18n
tmhDynamicLocale.set(language.replace('_', '-').toLowerCase());
});
// set locale for angular-i18n
tmhDynamicLocale.set(language.replace('_', '-').toLowerCase());
});

var params = $location.search();
Expand Down
31 changes: 14 additions & 17 deletions scripts/core/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import gettextjs from 'gettext.js';
import {debugInfo, getUserInterfaceLanguage} from 'appConfig';
import {getUserInterfaceLanguage} from 'appConfig';
import {IVocabularyItem, IArticle} from 'superdesk-api';
import {assertNever} from './helpers/typescript-helpers';
import {appConfig} from 'appConfig';

export type IScopeApply = (fn: () => void) => void;

export const i18n = gettextjs();

if (window.language != null && window.translations != null) {
i18n.setMessages(
'messages',
window.language,
window.translations,
window.pluralForms,
);

i18n.setLocale(window.language);
}

export type IScopeApply = (fn: () => void) => void;

export function stripHtmlTags(value) {
const el = document.createElement('div');

Expand Down Expand Up @@ -52,13 +63,6 @@ export const gettext = (
return '';
}

if (debugInfo.translationsLoaded !== true) {
console.warn(
`Invalid translation attempt for string "${text}": translation strings haven't been loaded yet.`
+ ' Original string will be displayed. \n' + new Error().stack.split('\n')[3].trim(),
);
}

let translated = i18n.gettext(text);

Object.keys(params ?? {}).forEach((param) => {
Expand Down Expand Up @@ -88,13 +92,6 @@ export const gettextPlural = (
return '';
}

if (debugInfo.translationsLoaded !== true) {
console.warn(
`Invalid translation attempt for string "${text}": translation strings haven't been loaded yet.`
+ ' Original string will be displayed. \n' + new Error().stack.split('\n')[3].trim(),
);
}

let translated = i18n.ngettext(text, pluralText, count);

Object.keys(params ?? {}).forEach((param) => {
Expand Down
3 changes: 3 additions & 0 deletions scripts/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ interface Window {
module: any;
RunTansaProofing: any;
iframely: any;
translations: any;
language: any;
pluralForms: any;
thecalcc marked this conversation as resolved.
Show resolved Hide resolved
}

// Allow importing json/html/svg files
Expand Down
55 changes: 1 addition & 54 deletions scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ import 'core';
import 'templates-cache.generated'; // generated by grunt 'ngtemplates' task
import 'apps';
import 'external-apps';
import {appConfig, getUserInterfaceLanguage, debugInfo} from 'appConfig';
import {appConfig} from 'appConfig';
import {IConfigurableUiComponents, IConfigurableAlgorithms} from 'superdesk-api';
import {CC} from 'core/ui/configurable-ui-components';
import {IExtensionLoader, registerExtensions} from 'core/register-extensions';
import {setupTansa} from 'apps/tansa';
import {i18n} from 'core/utils';
import {configurableAlgorithms} from 'core/ui/configurable-algorithms';
import {merge} from 'lodash';
import {maybeDisplayInvalidInstanceConfigurationMessage} from 'validate-instance-configuration';
Expand All @@ -40,53 +39,6 @@ function loadConfigs() {
});
}

export function loadTranslations() {
const language = getUserInterfaceLanguage();

return (() => {
if (language === 'en') {
return Promise.resolve({'': {'language': 'en', 'plural-forms': 'nplurals=2; plural=(n != 1);'}});
} else {
const filename = `/languages/${language}.json?nocache=${Date.now()}`;

return fetch(filename)
.then((response) => response.json())
.then((translations) => {
if (
translations[''] == null
|| translations['']['language'] == null
|| translations['']['plural-forms'] == null
) {
throw new Error(`Language metadata not found in "${filename}"`);
}

return translations;
});
}
})().then((translations) => {
const langOverride = appConfig.langOverride ?? {};
const pluralForms = translations['']['plural-forms'];

if (langOverride[language] != null) {
Object.assign(translations, langOverride[language]);
}

i18n.setMessages(
'messages',
language,
translations,
pluralForms,
);

i18n.setLocale(language);

return {
translations,
language,
};
});
}

let started = false;

function isDateFormatValid() {
Expand Down Expand Up @@ -189,11 +141,6 @@ export function startApp(
]);

loadConfigs()
.then(
() => loadTranslations().then(() => {
debugInfo.translationsLoaded = true;
}),
)
.then(() => {
if (isDateFormatValid() !== true) {
document.write('Invalid date format specified in config.view.dateFormat');
Expand Down
3 changes: 0 additions & 3 deletions scripts/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import 'apps';

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import {debugInfo} from 'appConfig';

debugInfo.translationsLoaded = true; // don't print warnings about missing translations when running unit tests

Enzyme.configure({adapter: new Adapter()});

Expand Down
52 changes: 52 additions & 0 deletions scripts/translations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const conf = __SUPERDESK_CONFIG__;

// We have the same function in `scripts/appConfig.ts`
// defined because there we can't export this one
// FIXME: When changing one or the other
// update both functions so they are the same
function getUserInterfaceLanguage() {
const user = JSON.parse(localStorage.getItem('sess:user'));
const language = user?.language ?? conf.default_language ?? window.navigator.language ?? 'en';

if (conf.profileLanguages?.includes(language)) {
return language;
} else {
return 'en';
}
}

function requestListener() {
const translations = JSON.parse(this.responseText);

if (translations[''] == null || translations['']['language'] == null || translations['']['plural-forms'] == null) {
throw new Error(`Language metadata not found in "${filename}"`);
}

const langOverride = conf.langOverride ?? {};
const pluralForms = translations['']['plural-forms'];

if (langOverride[language] != null) {
Object.assign(translations, langOverride[language]);
}

window.language = language;
window.translations = translations;
window.pluralForms = pluralForms;
}

const language = getUserInterfaceLanguage();

if (language === 'en') {
const translations = {'': {'language': 'en', 'plural-forms': 'nplurals=2; plural=(n != 1);'}};

window.language = translations['']['language'];
thecalcc marked this conversation as resolved.
Show resolved Hide resolved
window.translations = translations;
window.pluralForms = translations['']['plural-forms'];
} else {
const filename = `/languages/${language}.json?nocache=${Date.now()}`;
const req = new XMLHttpRequest();

req.addEventListener('load', requestListener);
req.open('GET', filename, false);
req.send();
}
5 changes: 4 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ module.exports = function makeConfig(grunt) {

return {
entry: {
app: [path.join(__dirname, 'scripts', 'index')],
app: [
path.join(__dirname, 'scripts', 'translations.js'),
thecalcc marked this conversation as resolved.
Show resolved Hide resolved
path.join(__dirname, 'scripts', 'index'),
],
},

output: {
Expand Down