2021-03-26 04:17:02 +04:30
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
2021-03-09 16:44:09 +03:30
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
2023-05-18 13:17:41 +02:00
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
2023-03-23 13:59:32 +01:00
|
|
|
import { t } from 'i18next';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { ISOLanguage, IsoLanguages } from '@/base/IsoLanguages.ts';
|
|
|
|
|
|
|
|
|
|
import { TranslationKey } from '@/base/Base.types.ts';
|
2021-03-09 16:44:09 +03:30
|
|
|
|
2023-04-21 00:27:42 +02:00
|
|
|
export enum DefaultLanguage {
|
|
|
|
|
ALL = 'all',
|
|
|
|
|
OTHER = 'other',
|
|
|
|
|
LOCAL_SOURCE = 'localsourcelang',
|
2025-05-17 22:17:15 +02:00
|
|
|
PINNED = 'pinned',
|
2025-05-17 23:37:00 +02:00
|
|
|
LAST_USED_SOURCE = 'last_used_source',
|
2023-04-21 00:27:42 +02:00
|
|
|
}
|
2021-09-18 00:48:48 +04:30
|
|
|
|
2025-05-09 01:08:44 +02:00
|
|
|
const DEFAULT_LANGUAGE_TO_TRANSLATION: Record<DefaultLanguage, TranslationKey> = {
|
|
|
|
|
[DefaultLanguage.ALL]: 'extension.language.all',
|
|
|
|
|
[DefaultLanguage.OTHER]: 'extension.language.other',
|
|
|
|
|
[DefaultLanguage.LOCAL_SOURCE]: 'extension.language.other',
|
2025-05-17 22:17:15 +02:00
|
|
|
[DefaultLanguage.PINNED]: 'global.label.pinned',
|
2025-05-17 23:37:00 +02:00
|
|
|
[DefaultLanguage.LAST_USED_SOURCE]: 'global.label.last_used',
|
2025-05-09 01:08:44 +02:00
|
|
|
};
|
|
|
|
|
|
2025-04-21 19:01:52 +02:00
|
|
|
type LanguageObject = ISOLanguage & { orgCode: string; isoCode: string };
|
|
|
|
|
|
2025-09-30 21:27:37 +02:00
|
|
|
function getISOLanguageFor(code: string, orgCode: string = code, isoCode: string = code): LanguageObject | null {
|
2024-10-05 16:09:34 +02:00
|
|
|
if (IsoLanguages[code]) {
|
2025-04-21 19:01:52 +02:00
|
|
|
return {
|
|
|
|
|
...IsoLanguages[code],
|
2025-09-30 21:27:37 +02:00
|
|
|
orgCode,
|
|
|
|
|
isoCode,
|
2025-04-21 19:01:52 +02:00
|
|
|
};
|
2024-03-09 02:52:34 +01:00
|
|
|
}
|
2021-03-09 18:05:34 +03:30
|
|
|
|
2024-03-09 02:52:34 +01:00
|
|
|
return null;
|
|
|
|
|
}
|
2021-03-09 18:05:34 +03:30
|
|
|
|
2025-09-30 21:27:37 +02:00
|
|
|
function getISOLanguage(code: string): LanguageObject | null {
|
|
|
|
|
return (
|
|
|
|
|
getISOLanguageFor(code) ??
|
|
|
|
|
getISOLanguageFor(code.toLowerCase(), code) ??
|
|
|
|
|
getISOLanguageFor(code.replace('-', '_'), code) ??
|
|
|
|
|
getISOLanguageFor(code.replace('_', '-'), code) ??
|
|
|
|
|
getISOLanguageFor(code.split('-')[0], code) ??
|
|
|
|
|
getISOLanguageFor(code.split('_')[0], code)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-21 19:01:52 +02:00
|
|
|
export function getLanguage(code: string): LanguageObject {
|
2024-03-09 02:52:34 +01:00
|
|
|
const isoLanguage = getISOLanguage(code);
|
|
|
|
|
|
|
|
|
|
if (isoLanguage) {
|
|
|
|
|
return isoLanguage;
|
2021-03-09 18:05:34 +03:30
|
|
|
}
|
|
|
|
|
|
2024-03-09 02:52:34 +01:00
|
|
|
return {
|
2025-04-21 19:01:52 +02:00
|
|
|
orgCode: code,
|
|
|
|
|
isoCode: code,
|
2024-03-09 02:52:34 +01:00
|
|
|
name: t('global.language.label.language_with_code', { code }),
|
|
|
|
|
nativeName: t('global.language.label.language_with_code', { code }),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 00:42:56 +02:00
|
|
|
export function languageCodeToName(code: string): string {
|
2025-05-09 01:08:44 +02:00
|
|
|
const isCustomLanguage = Object.keys(DEFAULT_LANGUAGE_TO_TRANSLATION).includes(code);
|
|
|
|
|
if (isCustomLanguage) {
|
|
|
|
|
return t(DEFAULT_LANGUAGE_TO_TRANSLATION[code as DefaultLanguage]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 02:52:34 +01:00
|
|
|
return getLanguage(code).nativeName;
|
2021-03-09 16:44:09 +03:30
|
|
|
}
|
|
|
|
|
|
2025-04-22 10:37:47 +02:00
|
|
|
export const toUniqueLanguageCodes = (codes: string[]): string[] => {
|
|
|
|
|
const languages = codes.map((code) => getLanguage(code));
|
|
|
|
|
const languagesByIsoCode = Object.groupBy(languages, (language) => language.isoCode);
|
|
|
|
|
|
|
|
|
|
return Object.entries(languagesByIsoCode)
|
|
|
|
|
.filter(([, languagesOfIsoCode]) => !!languagesOfIsoCode?.length)
|
|
|
|
|
.map(([, languagesOfIsoCode]) => languagesOfIsoCode![0].orgCode);
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-24 13:38:05 +02:00
|
|
|
export const toComparableLanguage = (code: string): string => getLanguage(code).isoCode;
|
|
|
|
|
|
|
|
|
|
export const toComparableLanguages = (codes: string[]): string[] => codes.map(toComparableLanguage);
|
|
|
|
|
|
2025-04-21 00:40:51 +02:00
|
|
|
function defaultNativeLang(): readonly string[] {
|
2025-04-22 10:37:47 +02:00
|
|
|
const preferredLanguages = toUniqueLanguageCodes([...navigator.languages]);
|
|
|
|
|
|
|
|
|
|
if (!preferredLanguages.length) {
|
|
|
|
|
return ['en'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return preferredLanguages;
|
2021-10-18 14:02:51 +03:30
|
|
|
}
|
|
|
|
|
|
2025-05-09 00:44:48 +02:00
|
|
|
export function getDefaultLanguages(): string[] {
|
|
|
|
|
return [...defaultNativeLang(), DefaultLanguage.ALL];
|
2021-03-09 16:44:09 +03:30
|
|
|
}
|
|
|
|
|
|
2025-05-09 01:08:44 +02:00
|
|
|
/**
|
|
|
|
|
* Sort languages by their native name.
|
|
|
|
|
* Custom languages are optionally treated specially:
|
|
|
|
|
* - All: first
|
|
|
|
|
* - Other: last
|
|
|
|
|
*/
|
|
|
|
|
export const languageSortComparator = (a: string, b: string, specialCustomLanguagesHandling?: boolean) => {
|
|
|
|
|
const isALanguageAll = a === DefaultLanguage.ALL;
|
|
|
|
|
const isALanguageOther = a === DefaultLanguage.OTHER || a === DefaultLanguage.LOCAL_SOURCE;
|
|
|
|
|
|
|
|
|
|
const isBLanguageAll = b === DefaultLanguage.ALL;
|
|
|
|
|
const isBLanguageOther = b === DefaultLanguage.OTHER || b === DefaultLanguage.LOCAL_SOURCE;
|
|
|
|
|
|
|
|
|
|
if (specialCustomLanguagesHandling) {
|
|
|
|
|
if (isALanguageAll || isBLanguageOther) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isALanguageOther || isBLanguageAll) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return languageCodeToName(a).localeCompare(languageCodeToName(b));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sort languages by their native name.
|
|
|
|
|
* Custom languages are treated specially:
|
|
|
|
|
* - All: first
|
|
|
|
|
* - Other: last
|
|
|
|
|
*/
|
|
|
|
|
export const languageSpecialSortComparator = (a: string, b: string) => languageSortComparator(a, b, true);
|