-
Notifications
You must be signed in to change notification settings - Fork 64
/
Copy pathi18n.js
142 lines (116 loc) · 4.6 KB
/
i18n.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
Copyright 2020 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.
This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
*/
import { useI18n } from 'vue-i18n';
import { computed } from 'vue';
import { localStore } from './storage';
import { locales } from '../i18n';
import { memoizeForContainer } from './composable';
// Returns the user's preferred locale based on their previous selection and
// their browser settings. Returns `null` if there is no locale that matches
// their preferences.
export const userLocale = () => {
const storageLocale = localStore.getItem('locale');
if (storageLocale != null && locales.has(storageLocale)) return storageLocale;
// Match on the language subtag, ignoring script and region.
const byLanguage = new Map();
for (const locale of locales.keys())
byLanguage.set(new Intl.Locale(locale).language, locale);
for (const locale of navigator.languages) {
const match = byLanguage.get(new Intl.Locale(locale).language);
if (match != null) return match;
}
return null;
};
////////////////////////////////////////////////////////////////////////////////
// loadLocale()
const setLocale = (i18n, locale) => {
i18n.locale = locale; // eslint-disable-line no-param-reassign
document.documentElement.setAttribute('lang', locale);
};
// Loads a locale asynchronously.
export const loadLocale = ({ i18n, logger }, locale) => {
if (!locales.has(locale)) return Promise.reject(new Error('unknown locale'));
if (i18n.messages[locale] != null) {
if (locale !== i18n.locale) setLocale(i18n, locale);
return Promise.resolve();
}
return import(
/* webpackChunkName: "i18n-[request]" */
`../locales/${locale}.json`
)
.then(m => {
i18n.setLocaleMessage(locale, m.default);
setLocale(i18n, locale);
})
.catch(error => {
logger.log(error);
throw error;
});
};
////////////////////////////////////////////////////////////////////////////////
// tn(), $tcn()
// Combination of $tc() and $n()
export function $tcn(path, count, values = undefined) {
return this.$tc(path, count, { count: this.$n(count, 'default'), ...values });
}
// Combination of t() and n()
const tn = (t, n) => (path, count, values) => {
const list = { count: n(count, 'default') };
Object.entries(values || {}).forEach(([k, v]) => { list[k] = typeof v === 'number' ? n(v, 'default') : v; });
return t(path, list, count);
};
////////////////////////////////////////////////////////////////////////////////
// useI18nUtils()
const listFormatOptions = {
default: { style: 'narrow' },
long: { style: 'long' }
};
const useGlobalUtils = memoizeForContainer(({ i18n }) => {
const numberFormats = {};
const getNumberFormat = (key) => {
const { locale } = i18n;
if (numberFormats[locale] == null) numberFormats[locale] = {};
const existingFormat = numberFormats[locale][key];
if (existingFormat != null) return existingFormat;
const options = i18n.getNumberFormat(locale)[key];
if (options == null) throw new Error('unknown key');
const numberFormat = new Intl.NumberFormat(locale, options);
numberFormats[locale][key] = numberFormat;
return numberFormat;
};
const listFormats = {};
const getListFormat = (key) => {
const { locale } = i18n;
if (listFormats[locale] == null) listFormats[locale] = {};
const existingFormat = listFormats[locale][key];
if (existingFormat != null) return existingFormat;
const options = listFormatOptions[key];
if (options == null) throw new Error('unknown key');
const listFormat = new Intl.ListFormat(locale, options);
listFormats[locale][key] = listFormat;
return listFormat;
};
return {
formatRange: (start, end, key = 'default') => (start === end
? i18n.n(start, key)
: getNumberFormat(key).formatRange(start, end)),
formatList: (list, key = 'default') => getListFormat(key).format(list),
formatListToParts: (list, key = 'default') =>
getListFormat(key).formatToParts(list),
sentenceSeparator: computed(() =>
locales.get(i18n.locale).sentenceSeparator)
};
});
const useLocalUtils = () => {
const { t, n } = useI18n();
return { tn: tn(t, n) };
};
export const useI18nUtils = () => ({ ...useGlobalUtils(), ...useLocalUtils() });