summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js437
1 files changed, 437 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js
new file mode 100644
index 0000000..d7e0ee8
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/foundation/wrappers/localization.js
@@ -0,0 +1,437 @@
+import { makeMetatype } from "@jet/environment/util/metatype";
+import { AmpLocalization } from "../amp-localization/amp-localization";
+import { isNullOrEmpty } from "../json-parsing/server-data";
+import { Wrapper } from "./wrapper";
+import { isSome } from "@jet/environment/types/optional";
+import * as validation from "@jet/environment/json/validation";
+/**
+ * Wrapper around an object or `Localization` type.
+ * The wrapped object is implemented as part of a native app.
+ */
+export class LocalizationWrapper extends Wrapper {
+ constructor(loc, objectGraph) {
+ super(loc);
+ /**
+ * Path to localization file.
+ * The path is set when the localization file is loaded
+ * for the first time.
+ */
+ this.locFile = null;
+ /**
+ * Instance of `AMPLocalization` providing server-side
+ * values for localized stirngs.
+ */
+ this.ampLoc = new AmpLocalization();
+ /**
+ * Localization strings cache.
+ * Cache and reuse values returned by
+ * wrapped `Localization` implementation.
+ */
+ this.LOC_STRING_CACHE = {};
+ this.objectGraph = objectGraph;
+ }
+ /**
+ * Returns the wrapped localization's identifier.
+ */
+ get identifier() {
+ return this.implementation.identifier;
+ }
+ /**
+ * Returns the wrapped localization's safe identifier.
+ */
+ get safeIdentifier() {
+ return this.implementation.identifier.split("_")[0];
+ }
+ // endregion
+ // region Localization
+ /**
+ * Localizes a string replacing placehoders in key with values in the parameters dictionary
+ * @param key The loc key to look up
+ * @param params Parameters to replace in the loc string
+ * @return The localized string
+ */
+ string(key, defaultValue) {
+ return this.implementation.string(key);
+ }
+ /**
+ * Localizes a string, and logs & throws an error if the key is a screamer.
+ * @param key The loc key to look up
+ * @return The localized string
+ */
+ tryString(key) {
+ const value = this.implementation.string(key);
+ if (value === key || value === `**${key}**`) {
+ validation.context("tryString", () => {
+ validation.unexpectedType("coercedValue", "Localization key", key, null);
+ });
+ throw new Error(`No value exists for localization key '${key}'`);
+ }
+ return value;
+ }
+ /**
+ * Localizes a string, and logs an error if the key is a screamer.
+ * @param key The loc key to look up
+ * @param fallback The fallback value for unlocalized keys, e.g. English value
+ * @return The localized string
+ */
+ stringWithFallback(key, fallback) {
+ const value = this.implementation.string(key);
+ return value === `**AppStore.${key}**` ? fallback : value;
+ }
+ /**
+ * Localizes a string using a preferred locale.
+ *
+ * The implementation of this function is similar to `string(key:)` above, but with the option
+ * to prefer a particular locale. If one is provided, we augment the lookup key in the cache
+ * with the `locale` value. If one is not provided, we fallback to `string(key:)`. This makes it
+ * much easier to use this function directly, in place of `string(key:)` where necessary.
+
+ * @param objectGraph The object graph, used to forward this call on if the required native function is unavailable.
+ * Can be removed for 2023.
+ * @param key The loc key to look up.
+ * @param locale The preferred locale to use for look up. Falls back to default, if unavailable.
+ * @param defaultValue A default value to use if nothing is found.
+ */
+ stringForPreferredLocale(objectGraph, key, locale, defaultValue) {
+ if (isNullOrEmpty(locale)) {
+ return this.string(key, defaultValue);
+ }
+ const cacheKey = `${key}_${locale}`;
+ let value = this.LOC_STRING_CACHE[cacheKey];
+ if (!value) {
+ value = this.implementation.stringForPreferredLocale(key, locale);
+ if (value && value !== key) {
+ this.LOC_STRING_CACHE[cacheKey] = value;
+ }
+ else {
+ const serverValue = this.ampLoc.localize(key);
+ if (serverValue !== key) {
+ value = serverValue;
+ }
+ else if (defaultValue) {
+ value = defaultValue;
+ }
+ else {
+ value = key;
+ }
+ }
+ }
+ return value;
+ }
+ /**
+ * Localize with appropriate plural form based on the count.
+ *
+ * Some languages have more plural forms than others.
+ * The full set of categories is "zero", "one", "two", "few", "many", and "other".
+ *
+ * The base loc key is used for "other" (the default).
+ * Otherwise the category is appended to the base loc key with a "." separator.
+ *
+ * http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
+ * http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories
+ * http://unicode.org/repos/cldr/trunk/specs/ldml/tr35-numbers.html#Language_Plural_Rules
+ *
+ * English loc keys
+ * key = "@@count@@ dogs"; // default, aka "plural"
+ * key.zero = "no dogs"; // used when count is 0
+ * key.one = "@@count@@ dog"; // used when count is 1, aka "singular"
+ *
+ * @param key base loc key
+ * @param count number used to determine the plural form
+ * @param params (optional) substitution keys and values, "count" will be added if it's not already present
+ * @return localized string
+ */
+ stringWithCount(key, count, params) {
+ let value = this.implementation.stringWithCount(key, count);
+ if (!value || value === key) {
+ const serverValue = this.ampLoc.localizeWithCount(this.objectGraph, key, count, params);
+ if (serverValue) {
+ value = serverValue;
+ }
+ }
+ return value;
+ }
+ /**
+ * A variation of `stringWithCount` that supports multiple plural forms.
+ * @param key base loc key
+ * @param counts numbers used to determine the plural forms
+ * @param params (optional) substitution keys and values, "count" will be added if it's not already present
+ * @return localized string
+ */
+ stringWithCounts(key, counts, params) {
+ return this.implementation.stringWithCounts(key, counts);
+ }
+ /**
+ * Converts a string into its uppercased form.
+ * @param value The string to apply the uppercase to.
+ * @returns {string} The loacle-specific, uppercased form of the string. If for whatever reason the upper-casing cannot
+ * be applied, this function simply returns the input value.
+ */
+ uppercased(value) {
+ if (!value) {
+ return null;
+ }
+ return value.toLocaleUpperCase(this.safeIdentifier);
+ }
+ /**
+ * Converts a number into a localized string
+ *
+ * @param n The number to convert
+ * @param decimalPlaces The number of decimal places to include.
+ * @return {string} The localized version of the number
+ */
+ decimal(n, decimalPlaces) {
+ let value = this.implementation.decimal(n, decimalPlaces);
+ if (!value) {
+ if (typeof n === "number") {
+ value = `* ${n.toString()} *`;
+ }
+ else {
+ value = this.nullString();
+ }
+ }
+ return value;
+ }
+ /**
+ * Converts a number of bytes into a localized file size string
+ *
+ * @param bytes The number of bytes to convert
+ * @return The localized file size string
+ */
+ fileSize(bytes) {
+ let value = this.implementation.fileSize(bytes);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ /**
+ * Converts a number of bytes into a localized file size string
+ *
+ * @param count The number of bytes to convert
+ * @return The localized file size string
+ */
+ formattedCount(count) {
+ let value = this.implementation.formattedCount(count);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ /**
+ * Converts a number into a formatted string representation using the preferred locale identifier.
+ *
+ * @param count The number to format.
+ * @param locale The locale identifier to prefer for lookup.
+ * @return The localized string.
+ */
+ formattedCountForPreferredLocale(objectGraph, count, locale) {
+ if (isNullOrEmpty(locale)) {
+ return this.formattedCount(count);
+ }
+ let value = this.implementation.formattedCountForPreferredLocale(count, locale);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ /**
+ * Converts a date into a time ago label, showing how long ago
+ * the date occurred
+ *
+ * @param date The date object to convert
+ * @param context The context in which the date should be formatted
+ * @return The localized string representing the amount of time that has passed
+ */
+ timeAgoWithContext(date, context) {
+ let value = this.implementation.timeAgoWithContext(date, context);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ /**
+ * Converts a date into a localized date string using the provided format
+ *
+ * @param format The format string describing how the date should be formatted
+ * @param date The date object to convert
+ * @return The localized string representing the date
+ */
+ formatDate(format, date) {
+ let value = this.implementation.formatDate(format, date);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ formatDateWithContext(format, date, context) {
+ let value = this.implementation.formatDateWithContext(format, date, context);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ formatDateInSentence(sentence, format, date) {
+ let value = this.implementation.formatDateInSentence(sentence, format, date);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ /**
+ * Converts a date into a relative date, showing how long ago
+ * the date occurred
+ *
+ * @param date The date object to convert
+ * @return The localized string representing the amount of time that has passed
+ */
+ relativeDate(date) {
+ let value = this.implementation.relativeDate(date);
+ if (!value) {
+ value = this.nullString();
+ }
+ return value;
+ }
+ formatDuration(value, unit) {
+ let result = this.implementation.formatDuration(value, unit);
+ if (!result) {
+ result = this.nullString();
+ }
+ return result;
+ }
+ // endregion
+ // region Private Methods
+ /**
+ * Applies a new localization file and localization dictionary as the new set of localizations
+ *
+ * @param file The file name of the file that is loaded
+ * @param localizations The localizations dictionary to use
+ * @param locale Current normalized locale which includes language code
+ */
+ applyLocalizations(file, localizations, locale) {
+ if (this.isLocFileLoaded(file)) {
+ return;
+ }
+ this.locFile = file;
+ // The first 2 characters of normalized locale are the language code.
+ this.ampLoc.updateLocalizationData(localizations, locale.slice(0, 2));
+ }
+ /**
+ * Checks if the named loc file is already loaded
+ * @param file The file name to check
+ * @return {boolean} True or false depending on whether the file name is loaded
+ */
+ isLocFileLoaded(file) {
+ return this.locFile === file;
+ }
+ /**
+ * Normalizes the given bag language into a locale.
+ * @param language The language to normalize.
+ * @param storefrontIdentifier The storefront identifier to use.
+ * @return A locale to use.
+ */
+ normalizedLocale(objectGraph, language, storefrontIdentifier) {
+ language = language.toLowerCase();
+ switch (language) {
+ case "yue-hant": {
+ // Country code from the bag is not available in JS, so we use the storefront.
+ const macauStoreFrontIdentifier = objectGraph.props.asString("macauStorefrontIdentifier");
+ if (typeof storefrontIdentifier === "string" &&
+ typeof macauStoreFrontIdentifier === "string" &&
+ storefrontIdentifier.indexOf(macauStoreFrontIdentifier) !== -1) {
+ return "zh-ma";
+ }
+ else {
+ return "zh-hk";
+ }
+ }
+ default:
+ return language;
+ }
+ }
+ nullString() {
+ return "* null *";
+ }
+ // endregion
+ // region API
+ /**
+ * Loads the localizations from the JetPack.
+ */
+ load(objectGraph) {
+ // Sanity check
+ if (objectGraph.bag.language === undefined || objectGraph.bag.language === null) {
+ throw new Error("Bag language is not available. Unable to load localizations.");
+ }
+ const locale = this.normalizedLocale(objectGraph, objectGraph.bag.language, objectGraph.client.storefrontIdentifier);
+ const locName = `local/${locale}`;
+ // Load localizations if needed
+ if (!this.isLocFileLoaded(locName)) {
+ const localizations = objectGraph.props.asDictionary(`localizations.${locale}`);
+ if (localizations !== undefined && localizations !== null) {
+ this.applyLocalizations(locName, localizations, locale);
+ }
+ else {
+ // Fallback to english
+ const fallbackLocalizations = objectGraph.props.asDictionary(`localizations.en-us`);
+ if (fallbackLocalizations !== undefined && fallbackLocalizations !== null) {
+ this.applyLocalizations(locName, fallbackLocalizations, locale);
+ }
+ }
+ }
+ }
+ /**
+ * Returns the localized name for the provided device type.
+ * The company policy seems to be to always use the branded,
+ * non-localized text, but this is just-in-case.
+ * @returns {string} The display name for the device.
+ */
+ deviceDisplayName(objectGraph) {
+ if (objectGraph.client.isVision && isSome(objectGraph.host.deviceMarketingFamilyName)) {
+ return objectGraph.host.deviceMarketingFamilyName;
+ }
+ if (objectGraph.host.deviceLocalizedModel) {
+ return objectGraph.host.deviceLocalizedModel;
+ }
+ // TODO: <rdar://problem/33575833> Jet: Remove brand loc fallbacks
+ switch (objectGraph.client.deviceType) {
+ case "phone":
+ const localizedPhoneName = this.string("IPHONE_BRAND_NAME");
+ if (localizedPhoneName === "IPHONE_BRAND_NAME") {
+ return "iPhone";
+ }
+ return localizedPhoneName;
+ case "pad":
+ const localizedPadName = this.string("IPAD_BRAND_NAME");
+ if (localizedPadName === "IPAD_BRAND_NAME") {
+ return "iPad";
+ }
+ return localizedPadName;
+ case "tv":
+ const localizedTvName = this.string("APPLE_TV_BRAND_NAME");
+ if (localizedTvName === "APPLE_TV_BRAND_NAME") {
+ return "Apple\u00a0TV";
+ }
+ return localizedTvName;
+ case "watch":
+ const localizedWatchName = this.string("APPLE_WATCH_BRAND_NAME");
+ if (localizedWatchName === "APPLE_WATCH_BRAND_NAME") {
+ return "Apple\u00a0Watch";
+ }
+ return localizedWatchName;
+ case "mac":
+ const localizedMacName = this.string("MAC_BRAND_NAME");
+ if (localizedMacName === "MAC_BRAND_NAME") {
+ return "Mac";
+ }
+ return localizedMacName;
+ default:
+ return null;
+ }
+ }
+}
+// region Properties
+/**
+ * Localization wrapper metatype to use with object graph.
+ */
+LocalizationWrapper.type = makeMetatype("app-store:loc-wrapper");
+//# sourceMappingURL=localization.js.map \ No newline at end of file