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

Allow retrieval from languageHashes using environment in order to side-load i18n assets without rebuilding the application #2703

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/config/app-config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { HomeConfig } from './homepage-config.interface';
import { MarkdownConfig } from './markdown-config.interface';
import { FilterVocabularyConfig } from './filter-vocabulary-config';
import { DiscoverySortConfig } from './discovery-sort.config';
import { LanguageHashesConfig } from './languageHashes-config.interface';
import {QualityAssuranceConfig} from './quality-assurance.config';

interface AppConfig extends Config {
Expand All @@ -36,6 +37,7 @@ interface AppConfig extends Config {
debug: boolean;
defaultLanguage: string;
languages: LangConfig[];
languageHashes: LanguageHashesConfig[];
browseBy: BrowseByConfig;
communityList: CommunityListConfig;
homePage: HomeConfig;
Expand Down
26 changes: 25 additions & 1 deletion src/config/config.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { red, blue, green, bold } from 'colors';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs';
import { load } from 'js-yaml';
import { join } from 'path';

Expand All @@ -9,6 +9,7 @@ import { DefaultAppConfig } from './default-app-config';
import { ServerConfig } from './server-config.interface';
import { mergeConfig } from './config.util';
import { isNotEmpty } from '../app/shared/empty.util';
import { LanguageHashesConfig } from './languageHashes-config.interface';

const CONFIG_PATH = join(process.cwd(), 'config');

Expand Down Expand Up @@ -231,6 +232,29 @@ export const buildAppConfig = (destConfigPath?: string): AppConfig => {
// apply build defined production
appConfig.production = env === 'production';

const useDynamicLanguageHashes = isNotEmpty(ENV('DYNAMIC_LANGUAGE_HASHES', true)) ? ENV('DYNAMIC_LANGUAGE_HASHES', true) : false;
mahnkong marked this conversation as resolved.
Show resolved Hide resolved
if (appConfig.production && useDynamicLanguageHashes) {
const i18nAssetsDir = 'dist/server/assets/i18n/';
const i18nFiles = readdirSync(i18nAssetsDir);
//sort all the i18nFiles by mtime descending
i18nFiles.sort(function(a, b) {
return statSync(i18nAssetsDir + b).mtime.getTime() - statSync(i18nAssetsDir + a).mtime.getTime();
});

i18nFiles.forEach(file => {
const match = file.match(/^(.+)\.(.+)\.json$/);
// only add language hash to config if no config exists for current language (ensuring the latest language file
// is used)
if (match && appConfig.languageHashes.filter(((languageHashConfig) => languageHashConfig.lang === match[1])).length === 0) {
const languageConfig: LanguageHashesConfig = {
lang: match[1],
md5: match[2]
};
appConfig.languageHashes.push(languageConfig);
}
});
}

// build base URLs
buildBaseUrl(appConfig.ui);
buildBaseUrl(appConfig.rest);
Expand Down
3 changes: 3 additions & 0 deletions src/config/default-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { HomeConfig } from './homepage-config.interface';
import { MarkdownConfig } from './markdown-config.interface';
import { FilterVocabularyConfig } from './filter-vocabulary-config';
import { DiscoverySortConfig } from './discovery-sort.config';
import { LanguageHashesConfig } from './languageHashes-config.interface';
import {QualityAssuranceConfig} from './quality-assurance.config';

export class DefaultAppConfig implements AppConfig {
Expand Down Expand Up @@ -243,6 +244,8 @@ export class DefaultAppConfig implements AppConfig {
{ code: 'uk', label: 'Yкраї́нська', active: true}
];

languageHashes: LanguageHashesConfig[] = [];

// Browse-By Pages
browseBy: BrowseByConfig = {
// Amount of years to display using jumps of one year (current year - oneYearLimit)
Expand Down
10 changes: 10 additions & 0 deletions src/config/languageHashes-config.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Config } from './config.interface';

/**
* An interface to represent a languageHash in the configuration. A LanguageHashConfig has a lang attribute which should be the official
* language code for the language (e.g. ‘fr’) and a md5 string representing the checksum of the language asset file.
*/
export interface LanguageHashesConfig extends Config {
lang: string;
md5: string;
}
2 changes: 2 additions & 0 deletions src/environments/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ export const environment: BuildConfig = {
active: false,
}],

languageHashes: [],

// Browse-By Pages
browseBy: {
// Amount of years to display using jumps of one year (current year - oneYearLimit)
Expand Down
6 changes: 5 additions & 1 deletion src/ngx-translate-loaders/translate-browser.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export class TranslateBrowserLoader implements TranslateLoader {
if (hasValue(messages)) {
return observableOf(messages);
} else {
const translationHash: string = environment.production ? `.${(process.env.languageHashes as any)[lang + '.json5']}` : '';
let translationHash = '';
if (environment.production) {
const languageHashesConfigFromEnvironment = environment.languageHashes.filter((languageHashConfig) => languageHashConfig.lang === lang);
translationHash = `.${languageHashesConfigFromEnvironment.length > 0 ? languageHashesConfigFromEnvironment[0].md5 : (process.env.languageHashes as any)[lang + '.json5']}`;
}
// If they're not available on the transfer state (e.g. when running in dev mode), retrieve
// them using HttpClient
return this.http.get(`${this.prefix}${lang}${translationHash}${this.suffix}`, { responseType: 'text' }).pipe(
Expand Down
5 changes: 4 additions & 1 deletion src/ngx-translate-loaders/translate-server.loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Observable, of as observableOf } from 'rxjs';
import { readFileSync } from 'fs';
import { TransferState } from '@angular/platform-browser';
import { NGX_TRANSLATE_STATE, NgxTranslateState } from './ngx-translate-state';
import { environment } from '../environments/environment';

/**
* A TranslateLoader for ngx-translate to parse json5 files server-side, and store them in the
Expand All @@ -23,7 +24,9 @@ export class TranslateServerLoader implements TranslateLoader {
* @param lang the language code
*/
public getTranslation(lang: string): Observable<any> {
const translationHash: string = (process.env.languageHashes as any)[lang + '.json5'];
const languageHashesConfigFromEnvironment = environment.languageHashes.filter((languageHashConfig) => languageHashConfig.lang === lang);
const translationHash = languageHashesConfigFromEnvironment.length > 0 ? languageHashesConfigFromEnvironment[0].md5 : (process.env.languageHashes as any)[lang + '.json5'];

// Retrieve the file for the given language, and parse it
const messages = JSON.parse(readFileSync(`${this.prefix}${lang}.${translationHash}${this.suffix}`, 'utf8'));
// Store the parsed messages in the transfer state so they'll be available immediately when the
Expand Down
Loading