MOBILE-4288 multilang: Use parent languages

main
Noel De Martin 2023-04-25 15:27:38 +02:00
parent db3a998f71
commit df34b812a4
2 changed files with 157 additions and 16 deletions

View File

@ -44,27 +44,34 @@ export class AddonFilterMultilangHandlerService extends CoreFilterDefaultHandler
options?: CoreFilterFormatTextOptions, // eslint-disable-line @typescript-eslint/no-unused-vars
siteId?: string, // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<string> {
let language = await CoreLang.getCurrentLanguage();
// Get available languages.
const regex = /<(?:lang|span)[^>]+lang="([a-zA-Z0-9_-]+)"[^>]*>.*?<\/(?:lang|span)>/img;
const languages: Set<string> = new Set();
let match: RegExpExecArray | null;
// Match the current language.
const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g');
while ((match = regex.exec(text))) {
const language = match[1].toLowerCase().replace(/_/g, '-');
if (!text.match(currentLangRegEx)) {
// Current lang not found. Try to find the first language.
const matches = text.match(anyLangRegEx);
if (matches?.[0]) {
language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)?.[1] || language;
currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)</(?:lang|span)>', 'g');
} else {
// No multi-lang tag found, stop.
return text;
}
languages.add(language);
}
// Extract contents of current language.
// Find language to use.
let language: string | undefined = await CoreLang.getCurrentLanguage();
if (!languages.has(language)) {
language = CoreLang.getParentLanguage();
}
if (!language) {
return text;
}
// Apply filter.
const anyLangRegEx = /<(lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>.*?<\/(lang|span)>/img;
const languageRegEx = language.replace(/-/g, '(?:-|_)');
const currentLangRegEx = new RegExp(`<(?:lang|span)[^>]+lang="${languageRegEx}"[^>]*>(.*?)</(?:lang|span)>`, 'img');
text = text.replace(currentLangRegEx, '$1');
// Delete the rest of languages
text = text.replace(anyLangRegEx, '');
return text;

View File

@ -0,0 +1,134 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { mockSingleton } from '@/testing/utils';
import { CoreLang, CoreLangProvider, multilangString } from '@services/lang';
describe('Lang', () => {
let lang: CoreLangProvider;
let currentLanguage: string;
let parentLanguage: string | undefined;
beforeEach(() => {
lang = new CoreLangProvider();
currentLanguage = 'en';
mockSingleton(CoreLang, {
getCurrentLanguage: () => Promise.resolve(currentLanguage),
getParentLanguage: () => parentLanguage,
});
});
it('filters multilang text', async () => {
await expectMultilangFilter('foo', 'foo');
await expectMultilangFilter(`
<span class="multilang" lang="es">Spanish</span>
<span class="multilang" lang="en">English</span>
<span class="multilang" lang="ja">Japanese</span>
<span class="multilang" lang="ES">(ES)</span>
<span class="multilang" lang="EN">(EN)</span>
<span class="multilang" lang="JA">(JA)</span>
<span lang="es" class="multilang">[Spain]</span>
<span lang="en" class="multilang">[United States]</span>
<span lang="ja" class="multilang">[Japan]</span>
text
`, 'English (EN) [United States] text');
await expectMultilangFilter(`
{mlang es}Spanish{mlang}
{mlang en}English{mlang}
{mlang ja}Japanese{mlang}
{mlang ES}(ES){mlang}
{mlang EN}(EN){mlang}
{mlang JA}(JA){mlang}
text
`, 'English (EN) text');
});
it('filters multilang text using regions', async () => {
currentLanguage = 'en-au';
await expectMultilangFilter(`
<span class="multilang" lang="en">English</span>
<span class="multilang" lang="en-US">English</span>
<span class="multilang" lang="en_US">(US)</span>
<span class="multilang" lang="en-AU">English</span>
<span class="multilang" lang="en_AU">(AU)</span>
text
`, 'English (AU) text');
await expectMultilangFilter(`
{mlang en}English{mlang}
{mlang en-US}English{mlang}
{mlang en_US}(US){mlang}
{mlang en-AU}English{mlang}
{mlang en_AU}(AU){mlang}
text
`, 'English (AU) text');
});
it('filters multilang text using the current language', async () => {
const multilangText = `
<span class="multilang" lang="es">Spanish</span>
<span class="multilang" lang="en">English</span>
<span class="multilang" lang="ja">Japanese</span>
text
`;
const multilang2Text = `
{mlang es}Spanish{mlang}
{mlang en}English{mlang}
{mlang ja}Japanese{mlang}
text
`;
currentLanguage = 'en';
await expectMultilangFilter(multilangText, 'English text');
await expectMultilangFilter(multilang2Text, 'English text');
currentLanguage = 'es';
await expectMultilangFilter(multilangText, 'Spanish text');
await expectMultilangFilter(multilang2Text, 'Spanish text');
});
it('filters multilang text using the parent language', async () => {
currentLanguage = 'ca';
parentLanguage = 'ja';
await expectMultilangFilter(`
<span class="multilang" lang="es">Spanish</span>
<span class="multilang" lang="en">English</span>
<span class="multilang" lang="ja">Japanese</span>
text
`, 'Japanese text');
await expectMultilangFilter(`
{mlang es}Spanish{mlang}
{mlang en}English{mlang}
{mlang ja}Japanese{mlang}
text
`, 'Japanese text');
});
/**
* Test multilang filter (normalizing whitespace).
*/
async function expectMultilangFilter(text: string, expected: string): Promise<void> {
const actual = await lang.filterMultilang(multilangString(text));
expect(actual.replace(/\s+/g, ' ').trim()).toEqual(expected);
}
});