@shoelace-style/localize 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.0
4
+
5
+ - Added `exists()` method to determine if a term and/or a fallback term exists [#17](https://github.com/shoelace-style/localize/issues/17)
6
+
7
+ ## 3.0.4
8
+
9
+ - Ensure return values of translation functions are always a string
10
+
3
11
  ## 3.0.3
4
12
 
5
13
  - Fixed a bug where regional locales stopped working
package/dist/index.d.ts CHANGED
@@ -8,6 +8,10 @@ export interface Translation {
8
8
  export interface DefaultTranslation extends Translation {
9
9
  [key: string]: any;
10
10
  }
11
+ export interface ExistsOptions {
12
+ lang: string;
13
+ includeFallback: boolean;
14
+ }
11
15
  export declare function registerTranslation(...translation: Translation[]): void;
12
16
  export declare function update(): void;
13
17
  export declare class LocalizeController<UserTranslation extends Translation = DefaultTranslation> implements ReactiveController {
@@ -17,7 +21,9 @@ export declare class LocalizeController<UserTranslation extends Translation = De
17
21
  hostDisconnected(): void;
18
22
  dir(): string;
19
23
  lang(): string;
20
- term<K extends keyof UserTranslation>(key: K, ...args: FunctionParams<UserTranslation[K]>): any;
24
+ private getTranslationData;
25
+ exists<K extends keyof UserTranslation>(key: K, options: Partial<ExistsOptions>): boolean;
26
+ term<K extends keyof UserTranslation>(key: K, ...args: FunctionParams<UserTranslation[K]>): string;
21
27
  date(dateToFormat: Date | string, options?: Intl.DateTimeFormatOptions): string;
22
28
  number(numberToFormat: number | string, options?: Intl.NumberFormatOptions): string;
23
29
  relativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;
package/dist/index.js CHANGED
@@ -49,13 +49,28 @@ export class LocalizeController {
49
49
  lang() {
50
50
  return `${this.host.lang || documentLanguage}`.toLowerCase();
51
51
  }
52
- term(key, ...args) {
52
+ getTranslationData(lang) {
53
53
  var _a, _b;
54
- const locale = new Intl.Locale(this.lang());
54
+ const locale = new Intl.Locale(lang);
55
55
  const language = locale === null || locale === void 0 ? void 0 : locale.language.toLowerCase();
56
56
  const region = (_b = (_a = locale === null || locale === void 0 ? void 0 : locale.region) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
57
57
  const primary = translations.get(`${language}-${region}`);
58
58
  const secondary = translations.get(language);
59
+ return { locale, language, region, primary, secondary };
60
+ }
61
+ exists(key, options) {
62
+ var _a;
63
+ const { primary, secondary } = this.getTranslationData((_a = options.lang) !== null && _a !== void 0 ? _a : this.lang());
64
+ options = Object.assign({ includeFallback: false }, options);
65
+ if ((primary && primary[key]) ||
66
+ (secondary && secondary[key]) ||
67
+ (options.includeFallback && fallback && fallback[key])) {
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+ term(key, ...args) {
73
+ const { primary, secondary } = this.getTranslationData(this.lang());
59
74
  let term;
60
75
  if (primary && primary[key]) {
61
76
  term = primary[key];
@@ -68,7 +83,7 @@ export class LocalizeController {
68
83
  }
69
84
  else {
70
85
  console.error(`No translation found for: ${String(key)}`);
71
- return key;
86
+ return String(key);
72
87
  }
73
88
  if (typeof term === 'function') {
74
89
  return term(...args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shoelace-style/localize",
3
- "version": "3.0.3",
3
+ "version": "3.1.0",
4
4
  "description": "A micro library for localizing custom elements using Lit's Reactive Controller model.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
package/src/index.ts CHANGED
@@ -12,6 +12,11 @@ export interface DefaultTranslation extends Translation {
12
12
  [key: string]: any;
13
13
  }
14
14
 
15
+ export interface ExistsOptions {
16
+ lang: string;
17
+ includeFallback: boolean;
18
+ }
19
+
15
20
  const connectedElements = new Set<HTMLElement>();
16
21
  const documentElementObserver = new MutationObserver(update);
17
22
  const translations: Map<string, Translation> = new Map();
@@ -25,9 +30,7 @@ documentElementObserver.observe(document.documentElement, {
25
30
  attributeFilter: ['dir', 'lang']
26
31
  });
27
32
 
28
- //
29
- // Registers one or more translations
30
- //
33
+ /** Registers one or more translations */
31
34
  export function registerTranslation(...translation: Translation[]) {
32
35
  translation.map(t => {
33
36
  const code = t.$code.toLowerCase();
@@ -48,9 +51,7 @@ export function registerTranslation(...translation: Translation[]) {
48
51
  update();
49
52
  }
50
53
 
51
- //
52
- // Updates all localized elements that are currently connected
53
- //
54
+ /** Updates all localized elements that are currently connected */
54
55
  export function update() {
55
56
  documentDirection = document.documentElement.dir || 'ltr';
56
57
  documentLanguage = document.documentElement.lang || navigator.language;
@@ -62,25 +63,25 @@ export function update() {
62
63
  });
63
64
  }
64
65
 
65
- //
66
- // Reactive controller
67
- //
68
- // To use this controller, import the class and instantiate it in a custom element constructor:
69
- //
70
- // private localize = new LocalizeController(this);
71
- //
72
- // This will add the element to the set and make it respond to changes to <html dir|lang> automatically. To make it
73
- // respond to changes to its own dir|lang properties, make it a property:
74
- //
75
- // @property() dir: string;
76
- // @property() lang: string;
77
- //
78
- // To use a translation method, call it like this:
79
- //
80
- // ${this.localize.term('term_key_here')}
81
- // ${this.localize.date('2021-12-03')}
82
- // ${this.localize.number(1000000)}
83
- //
66
+ /**
67
+ * Localize Reactive Controller for components built with Lit
68
+ *
69
+ * To use this controller, import the class and instantiate it in a custom element constructor:
70
+ *
71
+ * private localize = new LocalizeController(this);
72
+ *
73
+ * This will add the element to the set and make it respond to changes to <html dir|lang> automatically. To make it
74
+ * respond to changes to its own dir|lang properties, make it a property:
75
+ *
76
+ * @property() dir: string;
77
+ * @property() lang: string;
78
+ *
79
+ * To use a translation method, call it like this:
80
+ *
81
+ * ${this.localize.term('term_key_here')}
82
+ * ${this.localize.date('2021-12-03')}
83
+ * ${this.localize.number(1000000)}
84
+ */
84
85
  export class LocalizeController<UserTranslation extends Translation = DefaultTranslation>
85
86
  implements ReactiveController
86
87
  {
@@ -115,12 +116,39 @@ export class LocalizeController<UserTranslation extends Translation = DefaultTra
115
116
  return `${this.host.lang || documentLanguage}`.toLowerCase();
116
117
  }
117
118
 
118
- term<K extends keyof UserTranslation>(key: K, ...args: FunctionParams<UserTranslation[K]>) {
119
- const locale = new Intl.Locale(this.lang());
119
+ private getTranslationData(lang: string) {
120
+ const locale = new Intl.Locale(lang);
120
121
  const language = locale?.language.toLowerCase();
121
122
  const region = locale?.region?.toLowerCase() ?? '';
122
123
  const primary = <UserTranslation>translations.get(`${language}-${region}`);
123
124
  const secondary = <UserTranslation>translations.get(language);
125
+
126
+ return { locale, language, region, primary, secondary };
127
+ }
128
+
129
+ /** Determines if the specified term exists, optionally checking the fallback translation. */
130
+ exists<K extends keyof UserTranslation>(key: K, options: Partial<ExistsOptions>): boolean {
131
+ const { primary, secondary } = this.getTranslationData(options.lang ?? this.lang());
132
+
133
+ options = {
134
+ includeFallback: false,
135
+ ...options
136
+ };
137
+
138
+ if (
139
+ (primary && primary[key]) ||
140
+ (secondary && secondary[key]) ||
141
+ (options.includeFallback && fallback && fallback[key as keyof Translation])
142
+ ) {
143
+ return true;
144
+ }
145
+
146
+ return false;
147
+ }
148
+
149
+ /** Outputs a translated term. */
150
+ term<K extends keyof UserTranslation>(key: K, ...args: FunctionParams<UserTranslation[K]>): string {
151
+ const { primary, secondary } = this.getTranslationData(this.lang());
124
152
  let term: any;
125
153
 
126
154
  // Look for a matching term using regionCode, code, then the fallback
@@ -132,7 +160,7 @@ export class LocalizeController<UserTranslation extends Translation = DefaultTra
132
160
  term = fallback[key as keyof Translation];
133
161
  } else {
134
162
  console.error(`No translation found for: ${String(key)}`);
135
- return key;
163
+ return String(key);
136
164
  }
137
165
 
138
166
  if (typeof term === 'function') {
@@ -143,19 +171,19 @@ export class LocalizeController<UserTranslation extends Translation = DefaultTra
143
171
  }
144
172
 
145
173
  /** Outputs a localized date in the specified format. */
146
- date(dateToFormat: Date | string, options?: Intl.DateTimeFormatOptions) {
174
+ date(dateToFormat: Date | string, options?: Intl.DateTimeFormatOptions): string {
147
175
  dateToFormat = new Date(dateToFormat);
148
176
  return new Intl.DateTimeFormat(this.lang(), options).format(dateToFormat);
149
177
  }
150
178
 
151
179
  /** Outputs a localized number in the specified format. */
152
- number(numberToFormat: number | string, options?: Intl.NumberFormatOptions) {
180
+ number(numberToFormat: number | string, options?: Intl.NumberFormatOptions): string {
153
181
  numberToFormat = Number(numberToFormat);
154
182
  return isNaN(numberToFormat) ? '' : new Intl.NumberFormat(this.lang(), options).format(numberToFormat);
155
183
  }
156
184
 
157
185
  /** Outputs a localized time in relative format. */
158
- relativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions) {
186
+ relativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string {
159
187
  return new Intl.RelativeTimeFormat(this.lang(), options).format(value, unit);
160
188
  }
161
189
  }