Skip to content

Commit

Permalink
Merge pull request #3102 from centerofci/i18n-load-en-translations-pa…
Browse files Browse the repository at this point in the history
…rallely

[i18n] Load "en" translations parallely
  • Loading branch information
seancolsen authored Oct 25, 2023
2 parents 6e11407 + e30a065 commit da201e0
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 46 deletions.
41 changes: 39 additions & 2 deletions config/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,54 @@
from django.conf import settings
from django.templatetags.static import static


from mathesar.utils.frontend import get_manifest_data


def frontend_settings(request):
manifest_data = get_manifest_data()
development_mode = settings.MATHESAR_MODE == 'DEVELOPMENT'

i18n_settings = get_i18n_settings(manifest_data, development_mode)
frontend_settings = {
'development_mode': settings.MATHESAR_MODE == 'DEVELOPMENT',
'manifest_data': get_manifest_data(),
'development_mode': development_mode,
'manifest_data': manifest_data,
'live_demo_mode': getattr(settings, 'MATHESAR_LIVE_DEMO', False),
'live_demo_username': getattr(settings, 'MATHESAR_LIVE_DEMO_USERNAME', None),
'live_demo_password': getattr(settings, 'MATHESAR_LIVE_DEMO_PASSWORD', None),
**i18n_settings
}
# Only include development URL if we're in development mode.
if frontend_settings['development_mode'] is True:
frontend_settings['client_dev_url'] = settings.MATHESAR_CLIENT_DEV_URL

return frontend_settings


def get_i18n_settings(manifest_data, development_mode):
"""
Hard coding this for now
but will be taken from users model
and cookies later on
"""
display_language = 'en'
fallback_language = 'en'

client_dev_url = settings.MATHESAR_CLIENT_DEV_URL

if development_mode is True:
module_translations_file_path = f'{client_dev_url}/src/i18n/{display_language}/index.ts'
legacy_translations_file_path = f'{client_dev_url}/src/i18n/{display_language}/index.ts'
else:
try:
module_translations_file_path = static(manifest_data[display_language]["file"])
legacy_translations_file_path = static(manifest_data[f"{display_language}-legacy"]["file"])
except KeyError:
module_translations_file_path = static(manifest_data[fallback_language]["file"])
legacy_translations_file_path = static(manifest_data[f"{fallback_language}-legacy"]["file"])

return {
'module_translations_file_path': module_translations_file_path,
'legacy_translations_file_path': legacy_translations_file_path,
'display_language': display_language
}
6 changes: 6 additions & 0 deletions config/settings/common_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from decouple import Csv, config as decouple_config
from dj_database_url import parse as db_url
from django.utils.translation import gettext_lazy


# We use a 'tuple' with pipes as delimiters as decople naively splits the global
Expand Down Expand Up @@ -254,4 +255,9 @@ def pipe_delim(pipe_string):
# List of Template names that contains additional script tags to be added to the base template
BASE_TEMPLATE_ADDITIONAL_SCRIPT_TEMPLATES = []

# i18n
LANGUAGES = [
('en', gettext_lazy('English')),
('ja', gettext_lazy('Japanese')),
]
SALT_KEY = SECRET_KEY
24 changes: 19 additions & 5 deletions mathesar/templates/mathesar/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
{% block title %}Home{% endblock %}

{% block styles %}
{% if not development_mode %} {% for css_file in manifest_data.module_css %}
<link rel="stylesheet" href="{% static css_file %}" />
{% endfor %} {% endif %}
{% if not development_mode %}
{% for css_file in manifest_data.module_css %}
<link rel="stylesheet" href="{% static css_file %}" />
{% endfor %}
{% endif %}
{% endblock %}

{% block scripts %}
Expand All @@ -17,6 +19,8 @@
{% endfor %}
{% endif %}

<script type="module" src="{{ module_translations_file_path }}"></script>

{% if development_mode %}
<script type="module" src="{{ client_dev_url }}/@vite/client"></script>
<script type="module" src="{{ client_dev_url }}/src/main.ts"></script>
Expand Down Expand Up @@ -52,12 +56,22 @@
></script>
<script
nomodule
id="vite-legacy-entry"
id="vite-legacy-translations-slot"
data-src="{{ legacy_translations_file_path }}"
>
System.import(
document.getElementById("vite-legacy-translations-slot").getAttribute("data-src")
);
</script>
<script
nomodule
id="vite-legacy-js-slot"
data-src="{% static manifest_data.legacy_js %}"
>
System.import(
document.getElementById("vite-legacy-entry").getAttribute("data-src")
document.getElementById("vite-legacy-js-slot").getAttribute("data-src")
);
</script>

{% endif %}
{% endblock %}
5 changes: 4 additions & 1 deletion mathesar/utils/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ def get_manifest_data():
module_data = raw_data['src/main.ts']
manifest_data['module_css'] = [filename for filename in module_data['css']]
manifest_data['module_js'] = module_data['file']

legacy_data = raw_data['src/main-legacy.ts']
manifest_data['legacy_polyfill_js'] = raw_data['vite/legacy-polyfills-legacy']['file']
manifest_data['legacy_js'] = legacy_data['file']

for locale, _ in settings.LANGUAGES or []:
manifest_data[locale] = raw_data[f'src/i18n/{locale}/index.ts']
manifest_data[f"{locale}-legacy"] = raw_data[f'src/i18n/{locale}/index-legacy.ts']

# Cache data for 1 hour
cache.set('manifest_data', manifest_data, 60 * 60)
return manifest_data
36 changes: 20 additions & 16 deletions mathesar_ui/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,42 @@
import { preloadCommonData } from '@mathesar/utils/preloadData';
import AppContext from './AppContext.svelte';
import RootRoute from './routes/RootRoute.svelte';
import { loadLocaleAsync } from './i18n/i18n-load';
import { setLocale } from './i18n/i18n-svelte';
import type { RequestStatus } from './api/utils/requestUtils';
import { getErrorMessage } from './utils/errors';
import ErrorBox from './components/message-boxes/ErrorBox.svelte';
import { loadLocaleAsync, loadTranslations } from './i18n/i18n-load';
let isTranslationsLoaded = false;
/**
* Later the translations file will be loaded
* in parallel to the FE's first chunk
* Why translations are being read from window object?
* In order to -
* 1. Load the translations file in parallel to the first FE chunk.
* 2. And then make it available for the entry(App.svelte)
* file to load them into memory.
*
* The index.html loads it as using a script tag
* Each translations file on load, attaches the translations
* to the window object
*/
let translationLoadStatus: RequestStatus = { state: 'processing' };
void (async () => {
try {
const { translations, displayLanguage } = window.Mathesar || {};
if (translations && displayLanguage) {
loadTranslations(displayLanguage, translations[displayLanguage]);
setLocale(displayLanguage);
isTranslationsLoaded = true;
} else {
await loadLocaleAsync('en');
setLocale('en');
translationLoadStatus = { state: 'success' };
} catch (exp) {
translationLoadStatus = {
state: 'failure',
errors: [getErrorMessage(exp)],
};
isTranslationsLoaded = true;
}
})();
const commonData = preloadCommonData();
</script>

{#if translationLoadStatus.state === 'success' && commonData}
{#if isTranslationsLoaded && commonData}
<AppContext {commonData}>
<RootRoute {commonData} />
</AppContext>
{:else if translationLoadStatus.state === 'processing'}
{:else if !isTranslationsLoaded}
<div class="app-loader">
<Spinner size="2rem" />
</div>
Expand Down
9 changes: 9 additions & 0 deletions mathesar_ui/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ declare module '*.mdx' {
const value: string;
export default value;
}

interface Window {
Mathesar:
| {
displayLanguage: Locales;
translations: Record<Locales, Translations> | undefined;
}
| undefined;
}
5 changes: 4 additions & 1 deletion mathesar_ui/src/i18n/en/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { BaseTranslation } from '../i18n-types.js';
import type { BaseTranslation, Translations } from '../i18n-types';
import { addTranslationsToGlobalObject } from '../i18n-util';

const en: BaseTranslation = {
general: {
Expand Down Expand Up @@ -32,3 +33,5 @@ const en: BaseTranslation = {
};

export default en;

addTranslationsToGlobalObject('en', en as Translations);
2 changes: 1 addition & 1 deletion mathesar_ui/src/i18n/formatters.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FormattersInitializer } from 'typesafe-i18n';
import type { Locales, Formatters } from './i18n-types.js';
import type { Locales, Formatters } from './i18n-types';

export const initFormatters: FormattersInitializer<
Locales,
Expand Down
11 changes: 8 additions & 3 deletions mathesar_ui/src/i18n/i18n-load.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initFormatters } from './formatters.js';
import type { Locales, Translations } from './i18n-types.js';
import { loadedFormatters, loadedLocales } from './i18n-util.js';
import { initFormatters } from './formatters';
import type { Locales, Translations } from './i18n-types';
import { loadedFormatters, loadedLocales } from './i18n-store';

const localeTranslationLoaders = {
ja: () => import('./ja/index.js'),
Expand Down Expand Up @@ -30,3 +30,8 @@ export async function loadLocaleAsync(locale: Locales): Promise<void> {
updateTranslationsDictionary(locale, await importLocaleAsync(locale));
loadFormatters(locale);
}

export function loadTranslations(locale: Locales, translations: Translations) {
updateTranslationsDictionary(locale, translations);
loadFormatters(locale);
}
11 changes: 11 additions & 0 deletions mathesar_ui/src/i18n/i18n-store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Formatters, Locales, Translations } from './i18n-types';

export const loadedLocales: Record<Locales, Translations> = {} as Record<
Locales,
Translations
>;

export const loadedFormatters: Record<Locales, Formatters> = {} as Record<
Locales,
Formatters
>;
4 changes: 2 additions & 2 deletions mathesar_ui/src/i18n/i18n-svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type {
Locales,
TranslationFunctions,
Translations,
} from './i18n-types.js';
import { loadedFormatters, loadedLocales } from './i18n-util.js';
} from './i18n-types';
import { loadedFormatters, loadedLocales } from './i18n-store';

const { locale, LL, setLocale } = initI18nSvelte<
Locales,
Expand Down
34 changes: 23 additions & 11 deletions mathesar_ui/src/i18n/i18n-util.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { initExtendDictionary } from 'typesafe-i18n/utils';
import type { Formatters, Locales, Translations } from './i18n-types.js';

export const loadedLocales: Record<Locales, Translations> = {} as Record<
Locales,
Translations
>;

export const loadedFormatters: Record<Locales, Formatters> = {} as Record<
Locales,
Formatters
>;
import type { Locales, Translations } from './i18n-types';

export const extendDictionary = initExtendDictionary<Translations>();

export function addTranslationsToGlobalObject(
locale: Locales,
translations: Translations,
) {
/**
* This function is being called by all of the translations files
* The base translation file is being loaded by the typesafe-i18n utility
* to generate types during the development time.
* Hence this function also runs in the context of node
* instead of just browser.
*/
if (typeof window === 'undefined') return;
window.Mathesar = {
...window.Mathesar,
displayLanguage: locale,
translations: {
...window.Mathesar?.translations,
[locale]: translations,
},
};
}
11 changes: 8 additions & 3 deletions mathesar_ui/src/i18n/ja/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import en from '../en/index.js';
import type { Translation } from '../i18n-types.js';
import { extendDictionary } from '../i18n-util.js';
import en from '../en/index';
import type { Translation } from '../i18n-types';
import {
addTranslationsToGlobalObject,
extendDictionary,
} from '../i18n-util.js';

const ja = extendDictionary(en, {}) as Translation;

export default ja;

addTranslationsToGlobalObject('en', ja);
6 changes: 5 additions & 1 deletion mathesar_ui/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ export default defineConfig({
build: {
manifest: true,
rollupOptions: {
input: './src/main.ts',
input: {
main: './src/main.ts',
en: './src/i18n/en/index.ts',
ja: './src/i18n/ja/index.ts',
},
},
outDir: '../mathesar/static/mathesar/',
emptyOutDir: true,
Expand Down

0 comments on commit da201e0

Please sign in to comment.