Modernize the i18n code.

- Remove old deprecated API methods `systemLanguage`, `browserLanguage`
  and `userLanguage`.
- Add types via JSDoc
This commit is contained in:
JC Brand 2023-06-29 09:37:21 +02:00
parent e18fdd56b1
commit 5310021b67
3 changed files with 59 additions and 50 deletions

View File

@ -2,6 +2,8 @@
## 10.1.5 (Unreleased)
- #3209: Fix error when importing the `converse` global with bootstrap modal API.
## 10.1.4 (2023-06-25)
- Fix `dist` directory not included in NPM package

View File

@ -28,6 +28,7 @@
});
converse.initialize({
i18n: 'af',
theme: 'dracula',
auto_away: 300,
enable_smacks: true,

View File

@ -5,53 +5,48 @@
* @description This is the internationalization module
*/
import Jed from 'jed';
import log from "@converse/headless/log.js";
import log from '@converse/headless/log.js';
import { _converse, api, converse, i18n } from '@converse/headless/core.js';
const { dayjs } = converse.env;
let jed_instance;
function detectLocale (library_check) {
/* Determine which locale is supported by the user's system as well
* as by the relevant library (e.g. converse.js or dayjs).
* @param { Function } library_check - Returns a boolean indicating whether
* the locale is supported.
*/
let locale;
if (window.navigator.userLanguage) {
locale = isLocaleAvailable(window.navigator.userLanguage, library_check);
}
if (window.navigator.languages && !locale) {
for (let i=0; i<window.navigator.languages.length && !locale; i++) {
locale = isLocaleAvailable(window.navigator.languages[i], library_check);
}
}
if (window.navigator.browserLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.browserLanguage, library_check);
}
if (window.navigator.language && !locale) {
locale = isLocaleAvailable(window.navigator.language, library_check);
}
if (window.navigator.systemLanguage && !locale) {
locale = isLocaleAvailable(window.navigator.systemLanguage, library_check);
}
return locale || 'en';
}
/**
* @private
* @param { string } locale
* @param { string[] } supported_locales
*/
function isConverseLocale (locale, supported_locales) {
return typeof locale === 'string' && supported_locales.includes(locale);
}
/**
* Determines which locale is supported by the user's system as well
* as by the relevant library (e.g. converse.js or dayjs).
* @private
* @param { string } preferred_locale
* @param { Function } isSupportedByLibrary - Returns a boolean indicating whether
* the locale is supported.
* @returns { string }
*/
function getLocale (preferred_locale, isSupportedByLibrary) {
if (typeof preferred_locale === 'string') {
if (preferred_locale === 'en' || isSupportedByLibrary(preferred_locale)) {
return preferred_locale;
}
if (preferred_locale === 'en' || isSupportedByLibrary(preferred_locale)) {
return preferred_locale;
}
return detectLocale(isSupportedByLibrary) || 'en';
const { languages } = window.navigator;
let locale;
for (let i = 0; i < languages.length && !locale; i++) {
locale = isLocaleAvailable(languages[i], isSupportedByLibrary);
}
return locale || 'en';
}
/* Check whether the locale or sub locale (e.g. en-US, en) is supported.
/**
* Check whether the locale or sub locale (e.g. en-US, en) is supported.
* @private
* @param { String } locale - The locale to check for
* @param { Function } available - Returns a boolean indicating whether the locale is supported
*/
@ -59,49 +54,60 @@ function isLocaleAvailable (locale, available) {
if (available(locale)) {
return locale;
} else {
var sublocale = locale.split("-")[0];
var sublocale = locale.split('-')[0];
if (sublocale !== locale && available(sublocale)) {
return sublocale;
}
}
}
/**
* Given a locale, return the closest locale returned by dayJS
* @private
* @param { string } locale
*/
function getDayJSLocale (locale) {
const dayjs_locale = locale.toLowerCase().replace('_', '-');
return dayjs_locale === 'ug' ? 'ug-cn' : dayjs_locale;
}
/* Fetch the translations for the given local at the given URL.
/**
* Fetch the translations for the given local at the given URL.
* @private
* @method i18n#fetchTranslations
* @param { _converse }
* @returns { Jed }
*/
async function fetchTranslations (_converse) {
async function fetchTranslations () {
const { api, locale } = _converse;
const dayjs_locale = getDayJSLocale(locale);
if (!isConverseLocale(locale, api.settings.get("locales")) || locale === 'en') {
if (!isConverseLocale(locale, api.settings.get('locales')) || locale === 'en') {
return;
}
const { default: data } = await import(/*webpackChunkName: "locales/[request]" */ `../i18n/${locale}/LC_MESSAGES/converse.po`);
const { default: data } = await import(
/*webpackChunkName: "locales/[request]" */ `../i18n/${locale}/LC_MESSAGES/converse.po`
);
await import(/*webpackChunkName: "locales/dayjs/[request]" */ `dayjs/locale/${dayjs_locale}.js`);
dayjs.locale(getLocale(dayjs_locale, l => dayjs.locale(l)));
jed_instance = new Jed(data);
dayjs.locale(getLocale(dayjs_locale, (l) => dayjs.locale(l)));
return new Jed(data);
}
let jed_instance;
/**
* @namespace i18n
*/
Object.assign(i18n, {
/**
* @param { string } preferred_locale
* @param { string[] } available_locales
*/
getLocale (preferred_locale, available_locales) {
return getLocale(preferred_locale, preferred => isConverseLocale(preferred, available_locales));
return getLocale(preferred_locale, (preferred) => isConverseLocale(preferred, available_locales));
},
/**
* @param { string } str - The string to be translated
*/
translate (str) {
if (!jed_instance) {
return Jed.sprintf.apply(Jed, arguments);
@ -120,8 +126,8 @@ Object.assign(i18n, {
} else {
try {
const preferred_locale = api.settings.get('i18n');
_converse.locale = i18n.getLocale(preferred_locale, api.settings.get("locales"));
await fetchTranslations(_converse);
_converse.locale = i18n.getLocale(preferred_locale, api.settings.get('locales'));
jed_instance = await fetchTranslations();
} catch (e) {
log.fatal(e.message);
_converse.locale = 'en';
@ -131,7 +137,7 @@ Object.assign(i18n, {
__ (...args) {
return i18n.translate(...args);
}
},
});
export const __ = i18n.__;