1e484b35bSopenharmony_ci/*
2e484b35bSopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
3e484b35bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4e484b35bSopenharmony_ci * you may not use this file except in compliance with the License.
5e484b35bSopenharmony_ci * You may obtain a copy of the License at
6e484b35bSopenharmony_ci *
7e484b35bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8e484b35bSopenharmony_ci *
9e484b35bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10e484b35bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11e484b35bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12e484b35bSopenharmony_ci * See the License for the specific language governing permissions and
13e484b35bSopenharmony_ci * limitations under the License.
14e484b35bSopenharmony_ci */
15e484b35bSopenharmony_ci
16e484b35bSopenharmony_ciimport {
17e484b35bSopenharmony_ci  isNull,
18e484b35bSopenharmony_ci  getValue,
19e484b35bSopenharmony_ci  Log
20e484b35bSopenharmony_ci} from '../../../utils/index';
21e484b35bSopenharmony_ci
22e484b35bSopenharmony_ciinterface I18nConstructor {
23e484b35bSopenharmony_ci  new(options: object): I18n;
24e484b35bSopenharmony_ci}
25e484b35bSopenharmony_ci
26e484b35bSopenharmony_ciexport interface I18nInterface {
27e484b35bSopenharmony_ci  instance?: { I18n: I18nConstructor };
28e484b35bSopenharmony_ci}
29e484b35bSopenharmony_ci
30e484b35bSopenharmony_ciinterface VMInterface {
31e484b35bSopenharmony_ci  $t: Function;
32e484b35bSopenharmony_ci  $tc: Function;
33e484b35bSopenharmony_ci  _i18n: Function;
34e484b35bSopenharmony_ci}
35e484b35bSopenharmony_ci
36e484b35bSopenharmony_ciconst instances = {};
37e484b35bSopenharmony_ci
38e484b35bSopenharmony_ci/**
39e484b35bSopenharmony_ci * This class provide internationalization support.
40e484b35bSopenharmony_ci */
41e484b35bSopenharmony_ciclass I18n {
42e484b35bSopenharmony_ci  public locale: string;
43e484b35bSopenharmony_ci  public messages: any;
44e484b35bSopenharmony_ci
45e484b35bSopenharmony_ci  constructor(options) {
46e484b35bSopenharmony_ci    this.locale = options.locale || language;
47e484b35bSopenharmony_ci    this.messages = options.messages;
48e484b35bSopenharmony_ci  }
49e484b35bSopenharmony_ci
50e484b35bSopenharmony_ci  /**
51e484b35bSopenharmony_ci   * Provide the '$t' method to import simple resources.
52e484b35bSopenharmony_ci   * @param {string} path - The path of language resources which to be translated.
53e484b35bSopenharmony_ci   * @param {*} [params] - The values of placeholder.
54e484b35bSopenharmony_ci   * @return {*} The translated result.
55e484b35bSopenharmony_ci   */
56e484b35bSopenharmony_ci  public $t(path: string, params?: any): any {
57e484b35bSopenharmony_ci    if (typeof path !== 'string') {
58e484b35bSopenharmony_ci      Log.warn(`Invalid parameter type: The type of 'path' should be string, not ${typeof path}.`);
59e484b35bSopenharmony_ci      return;
60e484b35bSopenharmony_ci    }
61e484b35bSopenharmony_ci    if (!this._hasMessage(this.messages)) {
62e484b35bSopenharmony_ci      return path;
63e484b35bSopenharmony_ci    }
64e484b35bSopenharmony_ci    let value = this._getMessage(this.messages, path);
65e484b35bSopenharmony_ci    if (isNull(value)) {
66e484b35bSopenharmony_ci      return path;
67e484b35bSopenharmony_ci    }
68e484b35bSopenharmony_ci    if (Object.prototype.toString.call(value) === '[object Object]' ||
69e484b35bSopenharmony_ci      Object.prototype.toString.call(value) === '[object Array]') {
70e484b35bSopenharmony_ci      return value;
71e484b35bSopenharmony_ci    }
72e484b35bSopenharmony_ci    value = this._translate(path, value, params);
73e484b35bSopenharmony_ci    return value;
74e484b35bSopenharmony_ci  }
75e484b35bSopenharmony_ci
76e484b35bSopenharmony_ci  /**
77e484b35bSopenharmony_ci   * Provide the '$tc' method to import singular and plural resources.
78e484b35bSopenharmony_ci   * @param {string} path - The path of language resources which to be translated.
79e484b35bSopenharmony_ci   * @param {number} [count] - The number which to be translated.
80e484b35bSopenharmony_ci   * @return {*} The translated result.
81e484b35bSopenharmony_ci   */
82e484b35bSopenharmony_ci  public $tc(path: string, count?: number): any {
83e484b35bSopenharmony_ci    if (typeof path !== 'string') {
84e484b35bSopenharmony_ci      Log.warn(`Invalid parameter type: The type of 'path' should be string, not ${typeof path}.`);
85e484b35bSopenharmony_ci      return;
86e484b35bSopenharmony_ci    }
87e484b35bSopenharmony_ci    if (typeof count !== 'number' && !isNull(count)) {
88e484b35bSopenharmony_ci      Log.warn(`Invalid parameter type: The type of 'count' should be number, not ${typeof count}.`);
89e484b35bSopenharmony_ci      return;
90e484b35bSopenharmony_ci    }
91e484b35bSopenharmony_ci    if (!this._hasMessage(this.messages)) {
92e484b35bSopenharmony_ci      return path;
93e484b35bSopenharmony_ci    }
94e484b35bSopenharmony_ci    let value = this._getMessage(this.messages, path);
95e484b35bSopenharmony_ci    if (isNull(value)) {
96e484b35bSopenharmony_ci      return path;
97e484b35bSopenharmony_ci    }
98e484b35bSopenharmony_ci    if (isNull(count)) {
99e484b35bSopenharmony_ci      count = 1;
100e484b35bSopenharmony_ci    }
101e484b35bSopenharmony_ci    value = this._getChoice(count, path, value);
102e484b35bSopenharmony_ci    value = this._translate(path, value, count);
103e484b35bSopenharmony_ci    return value;
104e484b35bSopenharmony_ci  }
105e484b35bSopenharmony_ci
106e484b35bSopenharmony_ci  /**
107e484b35bSopenharmony_ci  * Extend _i18n to Vm.
108e484b35bSopenharmony_ci  * @param {VMInterface} Vm - The Vm.
109e484b35bSopenharmony_ci  */
110e484b35bSopenharmony_ci  public extend(Vm: VMInterface): void {
111e484b35bSopenharmony_ci    Object.defineProperty(Vm, '_i18n', {
112e484b35bSopenharmony_ci      configurable: true,
113e484b35bSopenharmony_ci      enumerable: true,
114e484b35bSopenharmony_ci      get: function proxyGetter() {
115e484b35bSopenharmony_ci        return this.i18n ? this.i18n : global.aceapp.i18n;
116e484b35bSopenharmony_ci      }
117e484b35bSopenharmony_ci    });
118e484b35bSopenharmony_ci    Vm.$t = function(path, params) {
119e484b35bSopenharmony_ci      const i18n = this._i18n;
120e484b35bSopenharmony_ci      return i18n.$t(path, params);
121e484b35bSopenharmony_ci    };
122e484b35bSopenharmony_ci    Vm.$tc = function(path, count) {
123e484b35bSopenharmony_ci      const i18n = this._i18n;
124e484b35bSopenharmony_ci      return i18n.$tc(path, count);
125e484b35bSopenharmony_ci    };
126e484b35bSopenharmony_ci  }
127e484b35bSopenharmony_ci
128e484b35bSopenharmony_ci  private _hasMessage(message: object[]): boolean {
129e484b35bSopenharmony_ci    if (!message || message.length === 0) {
130e484b35bSopenharmony_ci      Log.debug('I18n message is null.');
131e484b35bSopenharmony_ci      return false;
132e484b35bSopenharmony_ci    }
133e484b35bSopenharmony_ci    return true;
134e484b35bSopenharmony_ci  }
135e484b35bSopenharmony_ci
136e484b35bSopenharmony_ci  private _getMessage(messages: any[], path: string): any {
137e484b35bSopenharmony_ci    for (const i in messages) {
138e484b35bSopenharmony_ci      const value = getValue(path, messages[i]);
139e484b35bSopenharmony_ci      if (!isNull(value)) {
140e484b35bSopenharmony_ci        return value;
141e484b35bSopenharmony_ci      }
142e484b35bSopenharmony_ci    }
143e484b35bSopenharmony_ci    return null;
144e484b35bSopenharmony_ci  }
145e484b35bSopenharmony_ci
146e484b35bSopenharmony_ci  private _getChoice(count: number, path: string, message: any): any {
147e484b35bSopenharmony_ci    const pluralChoice = i18nPluralRules.select(count);
148e484b35bSopenharmony_ci    if (!pluralChoice) {
149e484b35bSopenharmony_ci      Log.debug('PluralChoice is null.');
150e484b35bSopenharmony_ci      return path;
151e484b35bSopenharmony_ci    }
152e484b35bSopenharmony_ci    return getValue(pluralChoice, message);
153e484b35bSopenharmony_ci  }
154e484b35bSopenharmony_ci
155e484b35bSopenharmony_ci  private _translate(path: string, value: any, params: any): any {
156e484b35bSopenharmony_ci    if (isNull(value)) {
157e484b35bSopenharmony_ci      return path;
158e484b35bSopenharmony_ci    }
159e484b35bSopenharmony_ci    if (Object.prototype.toString.call(params) === '[object Array]') {
160e484b35bSopenharmony_ci      value = value.replace(/\{(\d+)\}/g, (_, index) => {
161e484b35bSopenharmony_ci        if (index > params.length - 1 || index < 0) {
162e484b35bSopenharmony_ci          return '';
163e484b35bSopenharmony_ci        }
164e484b35bSopenharmony_ci        return params[index];
165e484b35bSopenharmony_ci      });
166e484b35bSopenharmony_ci    } else if (Object.prototype.toString.call(params) === '[object Object]') {
167e484b35bSopenharmony_ci      value = value.replace(/\{(\w+)\}/g, (_, name) => {
168e484b35bSopenharmony_ci        if (name in params) {
169e484b35bSopenharmony_ci          return params[name];
170e484b35bSopenharmony_ci        }
171e484b35bSopenharmony_ci        return '';
172e484b35bSopenharmony_ci      });
173e484b35bSopenharmony_ci    } else if (Object.prototype.toString.call(params) === '[object Number]') {
174e484b35bSopenharmony_ci      value = value.replace(/\{count\}/g, params.toLocaleString(this.locale.replace('_', '-')));
175e484b35bSopenharmony_ci    } else {
176e484b35bSopenharmony_ci      return value;
177e484b35bSopenharmony_ci    }
178e484b35bSopenharmony_ci    return value;
179e484b35bSopenharmony_ci  }
180e484b35bSopenharmony_ci}
181e484b35bSopenharmony_ci
182e484b35bSopenharmony_ci/**
183e484b35bSopenharmony_ci * Init the i18n object.
184e484b35bSopenharmony_ci */
185e484b35bSopenharmony_ciexport default {
186e484b35bSopenharmony_ci  create: (id: number): I18nInterface | null => {
187e484b35bSopenharmony_ci    instances[id] = [];
188e484b35bSopenharmony_ci    if (typeof global.I18n === 'function') {
189e484b35bSopenharmony_ci      return {};
190e484b35bSopenharmony_ci    }
191e484b35bSopenharmony_ci    const i18nObject = {
192e484b35bSopenharmony_ci      I18n: class extends I18n {
193e484b35bSopenharmony_ci        constructor(options) {
194e484b35bSopenharmony_ci          super(options);
195e484b35bSopenharmony_ci          instances[id].push(this);
196e484b35bSopenharmony_ci        }
197e484b35bSopenharmony_ci      }
198e484b35bSopenharmony_ci    };
199e484b35bSopenharmony_ci    return {
200e484b35bSopenharmony_ci      instance: i18nObject
201e484b35bSopenharmony_ci    };
202e484b35bSopenharmony_ci  },
203e484b35bSopenharmony_ci  destroy: (id: number): void => {
204e484b35bSopenharmony_ci    delete instances[id];
205e484b35bSopenharmony_ci  }
206e484b35bSopenharmony_ci};
207