Skip to content

Commit

Permalink
feat: fallback locales
Browse files Browse the repository at this point in the history
  • Loading branch information
sdo-1A committed Nov 16, 2023
1 parent 6e12b35 commit dcb5407
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 13 deletions.
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,14 @@ Please refer to the details on how the ngx-prefetch works [here](docs/HOW_IT_WOR
If this is not the case, you can configure the full path of the resources that will be prefetched (ex: https://my-web-app.com/path/to/my-app/).
It is also possible to set this value at runtime. Instead of setting it in the Builder's options, you can search for `{STATICS_FULL_PATH}` and replace it on the server side in order to inject a path.
- `localizationPattern` Pattern for the relative path of the localization file. By default, the pattern corresponds to the JSON file in a folder called localizations: `"/localizations/${language}.json"`.
- `localizationPattern` Pattern for the relative path of the localization file. By default, the pattern corresponds to the JSON file in a folder called localizations: `"localizations/${language}.json"`.
If the localization pattern contains the `${language}` variable, the language value must be set (as explained [here](docs/HOW_IT_WORKS.md#localization)), and it will be replaced by the server.
- `fallbackLocalesMap` Mapping of fallback locales, in case the localization file of the input language cannot be found.
First, a search for an exact match of the input language will be done in the `fallbackLocalesMap`. If not found and the input language parameter is of type locale,
a search for the shortened version of the locale (for example, search for `en` if the input language is `en-GB`) will be done. If this is not found either, a search
for the default locale `*` will be searched for. If none of these are found within the dynamic content files, the localization file will not be prefetched.
You can find a detailed example [below](README.md#example-of-fallback-locale)
### Example of full configuration
Expand All @@ -111,7 +117,21 @@ Please refer to the details on how the ngx-prefetch works [here](docs/HOW_IT_WOR
"crossorigin": true,
"production": false,
"staticsFullPath": "https://my-web-app.com/path/to/my-app/",
"localizationPattern": "/localizations/${language}.json"
"localizationPattern": "localizations/${language}.json",
"fallbackLocalesMap": {
"fr-CA": "fr-FR",
"de": "de-DE",
"*": "en-GB"
}
}
}
```
#### Example of fallback locale
Let's assume you have the `fallbackLocalesMap` above in your configuration and three localization files in your assets: `fr-FR.json`, `de-DE.json`, and `en-GB.json`.
If your language parameter is equal to:
- `fr-FR`: you will prefetch this file directly
- `fr-CA`: you will fallback to `fr-FR`
- `de-AT`: you will fallback to `de-DE`
- `it-IT`: you will fallback to `en-GB` (the default fallback locale)
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default createBuilder<PrefetchBuilderSchema>(async (options, context): Pr

context.reportProgress(2, STEP_NUMBER, 'Read prefetch template file.');
const prefetchTemplate = fs.readFileSync(path.join(__dirname, 'templates', 'prefetch.mustache'), {encoding: 'utf-8'});
const configOptions = ['crossorigin', 'resourceTypes'];
const configOptions = ['crossorigin', 'resourceTypes', 'fallbackLocalesMap'];
const variables = {
resourceArray: JSON.stringify(resourceArray),
prefetchConfig: JSON.stringify(filterOptions(options, configOptions)),
Expand Down
13 changes: 12 additions & 1 deletion src/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,18 @@
"localizationPattern": {
"type": "string",
"description": "Pattern for the path of the localization file. By default, the pattern corresponds to the JSON file in a folder called localizations.",
"default": "/localizations/${language}.json"
"default": "localizations/${language}.json"
},
"fallbackLocalesMap": {
"type": "object",
"description": "List of fallback locales mapping",
"patternProperties": {
"([a-z]{2,3})(-([A-Z]{2}|[A-Za-z]{4,5}))?|([*])": {
"type": "string",
"pattern": "([a-z]{2,3})(-([A-Z]{2}|[A-Za-z]{4,5}))?"
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
Expand Down
3 changes: 3 additions & 0 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ export interface PrefetchBuilderSchema extends JsonObject {

/** Pattern for the path of the localization file. By default, the pattern corresponds to the JSON file in a folder called localizations. */
localizationPattern: string;

/** List of fallback locales mapping */
fallbackLocalesMap?: {[locale: string]: string};
}
49 changes: 41 additions & 8 deletions src/templates/prefetch.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,20 @@
document.head.appendChild(res);
}

function addLocalizationFile(locPattern) {
if (dynamicContentFiles.includes(locPattern)) {
resList.push(locPattern);
return true;
}
return false;
}

var resList = {{{resourceArray}}};
var staticsFullPath = '{{{staticsFullPath}}}';
var staticsFullPathKey = 'STATICS_FULL_PATH';
// for testing
if (staticsFullPath === `{${staticsFullPathKey}}`) { staticsFullPath = '.'; }

var language = '{LANG}';
var languageKey = 'LANG';
var locPattern = `{{{localizationPattern}}}`;
if (language !== `{${languageKey}}` && /^([a-z]{2,3})(-([A-Z]{2}|[A-Za-z]{4,5}))?$/.test(language)) {
resList.push(locPattern);
}

var dynamicContentPath = '{DYNAMIC_CONTENT_PATH}';
var dynamicContentPathKey = 'DYNAMIC_CONTENT_PATH';

Expand All @@ -51,12 +52,44 @@
}
}

var language = '{LANG}';
var languageKey = 'LANG';
if (language !== `{${languageKey}}` && /^([a-z]{2,3})(-([A-Z]{2}|[A-Za-z]{4,5}))?$/.test(language) && hasDynamicContent && dynamicContentFiles) {
if (dynamicContentFiles.includes(`{{{localizationPattern}}}`)) {
resList.push(`{{{localizationPattern}}}`);
} else if (prefetchConfig.fallbackLocalesMap) {
var fallback = false;
var inputLanguage = language;
if (prefetchConfig.fallbackLocalesMap[inputLanguage]) {
language = prefetchConfig.fallbackLocalesMap[inputLanguage];
fallback = addLocalizationFile(`{{{localizationPattern}}}`);
}
if (!fallback && inputLanguage.includes('-')) {
language = inputLanguage.split('-')[0];
if (prefetchConfig.fallbackLocalesMap[language]) {
language = prefetchConfig.fallbackLocalesMap[language];
fallback = addLocalizationFile(`{{{localizationPattern}}}`);
}
}
if (!fallback && prefetchConfig.fallbackLocalesMap['*']) {
language = prefetchConfig.fallbackLocalesMap['*'];
fallback = addLocalizationFile(`{{{localizationPattern}}}`);
}
if (!fallback) {
console.warn('Language, fallback language, and default language not found in dynamic content files.')
}
} else {
console.warn('Language not found in dynamic content files.')
}
}

resList.forEach(function(resource) {
if (typeof resource === 'string') {
var fullPath = staticsFullPath;
if (hasDynamicContent && dynamicContentFiles.includes(resource.slice(1))) {
fullPath = dynamicContentPath + '/';
fullPath = dynamicContentPath;
}
fullPath = fullPath + '/';
var splitRes = resource.split('.');
var extension = splitRes[splitRes.length - 1];
appendLink(fullPath + resource, getResType(extension));
Expand Down
2 changes: 1 addition & 1 deletion testing/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('Prefetch builder', () => {
crossorigin: true,
production: true,
staticsFullPath: '{STATICS_FULL_PATH}',
localizationPattern: '/localizations/${language}.json'
localizationPattern: 'localizations/${language}.json'
};

const expectedBuilderConfig = {
Expand Down

0 comments on commit dcb5407

Please sign in to comment.