gsheetcmslight
Version:
A library to read a Google Sheet with multilingal content
231 lines (215 loc) • 7.48 kB
JavaScript
var SheetValidator = function (options) {
this.CheckI8n = true;
if (options === undefined) return;
if (options.checkI8n !== undefined) this.CheckI8n = options.checkI8n;
if (options.sheet === undefined) throw new Error("Sheet is absent");
if (options.sheet === {}) throw new Error("Sheet is empty");
if (Object.keys(options.sheet).length === 0)
throw new Error("Sheet has not properties");
if (options.sheet.columnNames === undefined)
throw new Error("Sheet column names is not set");
if (options.sheet.columnNames.length === 0)
throw new Error("Sheet has no columns");
/**
* The sheet columns
*/
this.ColumnNames = options.sheet.columnNames;
/**
* The Configuration
*/
this.Config = options.config;
/**
* The default Value column name
*/
this.DEFAULT_COLUMN_NAME = "Value";
};
SheetValidator.prototype = {
/**
* Retrieve the first browser language
* @returns {string} The language
*/
RetrieveBrowserLang: function () {
const firstLang = new BrowserLanguageParser({
enableLog: this.enableLog,
}).GetBrowserFirstLang();
return firstLang;
},
/**
* Retrieve the language in the GET parameter "lang"
* @returns {string} The language
*/
RetrieveRequestLang: function () {
const requestLang = new QueryStringUtility({
enableLog: this.enableLog,
}).GetValue("lang");
return requestLang;
},
/**
* Decides which language that the app will used to retrieve the resource label.
* If the Google Sheet configuration has one set, it is used.
* Otherwise, the browser language is used.
* @returns {string} The language
*/
GetUsedLanguage: function () {
const browserLang = this.RetrieveBrowserLang();
const requestLang = this.RetrieveRequestLang();
const configDefaultLang = this.Config.DefaultLanguage;
const noLanguageDefined =
configDefaultLang === undefined &&
browserLang === undefined &&
requestLang === undefined;
if (noLanguageDefined && this.enableLog) {
console.warn("No language found in the Google Sheet or the Browser.");
}
//If request lang is read, return it
if (requestLang !== undefined) {
this.SetDocumentLang(requestLang);
return requestLang;
}
//If browser lang is read, return it
if (browserLang !== undefined) {
this.SetDocumentLang(browserLang);
return browserLang;
}
//If config lang is read, return it
if (configDefaultLang !== undefined) {
this.SetDocumentLang(configDefaultLang);
return configLang;
}
//Otherwise, return undefined.
return undefined;
},
/**
* Set the attribut lang in the HTML tag.
* @param {string} language The language value
*/
SetDocumentLang: function (language) {
const docLang = document.querySelector("html");
docLang.lang = language;
},
/**
* Builds the column name of the value's row to read from the browser language
* @returns {string}
*/
BuildExpectedI8nColumnName: function () {
const lang = this.GetUsedLanguage();
return `${this.DEFAULT_COLUMN_NAME}_${lang}`;
},
/**
* Builds the default column name of the value's row
* @returns {string}
*/
GetDefaultColumnValueName: function () {
return this.DEFAULT_COLUMN_NAME;
},
/**
* Finds the localized colunm name.
*
* @returns {string} The column name found or False
*/
GetI8nColumnExistsForUserLanguage: function () {
const I8nColumnName = this.BuildExpectedI8nColumnName();
return this.GetColumnName(this.ColumnNames, I8nColumnName);
},
GetValueColumnIdentity: function (sheet) {
if (!this.CheckI8n) return this.DEFAULT_COLUMN_NAME;
const i8nResult = this.GetI8nColumnExistsForUserLanguage();
if (i8nResult.isValid) return i8nResult.columnName;
console.info("Falling back to default column...");
const defaultResult = this.GetColumnName(
this.ColumnNames,
this.DEFAULT_COLUMN_NAME,
true
);
if (defaultResult.isValid) return defaultResult.columnName;
const errorMsg = new Error(
`The sheet "${sheet.name}" must at least contain a Value column`
);
throw new Error(errorMsg);
},
/**
* Search the column name matching the closest the input in a list.
*
* @param {array} avaibleColumns The columns available in a sheet
* @param {string} columnName The column to match against the row keys (which
* represent the colunm name of the sheet)
* @param {bool} matchExactly Use an exact match or loose filtering.
* @returns {string} The column matching the closest the input. Ex: if input
* was 'Value_fr and 'Value_fr-FR' existed, then 'Value_fr-FR' is returned.
* Otherwise, false is returned.
*/
GetColumnName: function (avaibleColumns, columnName, matchExactly = false) {
if (this.enableLog) console.log("Row keys", avaibleColumns);
var rowKeysFiltered = matchExactly
? this.FilterKeysExactly(columnName, avaibleColumns)
: this.FilterKeysLoosely(columnName, avaibleColumns);
if (this.enableLog) console.log("rowKeysFiltered", rowKeysFiltered);
let columnExists = rowKeysFiltered.length > 0;
if (!columnExists) {
if (this.enableLog)
console.log(
`The sheet doesn't contain any column matching the column '${columnName}'`
);
return { isValid: false };
}
return { isValid: true, columnName: rowKeysFiltered[0] };
},
/**
* Filter exactly matching the filter value.
*
* @param {string} filter The filter
* @param {array} array The array of values to filter
* @returns {array} The new array
*/
FilterKeysExactly: function (filter, array) {
if (filter === undefined) {
throw new Error("Parameter filter is required");
}
if (typeof filter !== "string") {
throw new Error("Parameter filter must be string");
}
if (array === undefined) {
throw new Error("Parameter array is required");
}
if (!Array.isArray(array)) {
throw new Error("Parameter array must be array");
}
if (array.length === 0) {
throw new Error("Parameter array must have values");
}
let arrayFiltered = [];
const regex = new RegExp("^" + filter.toLowerCase() + "$", "g");
array.reduce((accumulator, key) => {
return key.toLowerCase().match(regex)
? arrayFiltered.push(key)
: arrayFiltered;
});
return arrayFiltered;
},
/**
* Filter loosely matching the filter value.
*
* @param {string} filter The filter
* @param {array} array The array of values to filter
* @returns {array} The new array
*/
FilterKeysLoosely: function (filter, array) {
let arrayFiltered = [];
array.reduce((accumulator, key) => {
return key.toLowerCase().indexOf(filter.toLowerCase()) != -1
? arrayFiltered.push(key)
: arrayFiltered;
});
return arrayFiltered;
},
};
if (typeof module !== "undefined" && module.exports) {
//don't just use inNodeJS, we may be in Browserify
module.exports = SheetValidator;
} else if (typeof define === "function" && define.amd) {
define(function () {
return SheetValidator;
});
} else {
window.SheetValidator = SheetValidator;
}