Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Monster
Manage
Activity
Members
Plan
Jira
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
OSS
Libraries
Javascript
Monster
Commits
b976975d
Verified
Commit
b976975d
authored
4 months ago
by
Volker Schukai
Browse files
Options
Downloads
Patches
Plain Diff
feat: new language control #276
parent
7fa3cf45
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
source/i18n/util.mjs
+139
-0
139 additions, 0 deletions
source/i18n/util.mjs
with
139 additions
and
0 deletions
source/i18n/util.mjs
0 → 100644
+
139
−
0
View file @
b976975d
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import
{
languages
}
from
"
./map/languages.mjs
"
;
/**
* Determines the user's preferred language based on browser settings and available language options.
*
* It evaluates the current HTML document language, the browser's defined languages, and
* the language options from `<link>` elements with `hreflang` attributes in the document.
*
* @return {Object} An object containing information about the detected language, preferred language, and available languages.
*/
export
function
detectUserLanguagePreference
()
{
const
currentLang
=
document
.
documentElement
.
lang
;
let
preferredLanguages
=
[];
if
(
typeof
navigator
.
language
===
"
string
"
&&
navigator
.
language
.
length
>
0
)
{
preferredLanguages
=
[
navigator
.
language
];
}
if
(
Array
.
isArray
(
navigator
.
languages
)
&&
navigator
.
languages
.
length
>
0
)
{
preferredLanguages
=
navigator
.
languages
;
}
// add to preferredLanguages all the base languages of the preferred languages
preferredLanguages
=
preferredLanguages
.
concat
(
preferredLanguages
.
map
(
lang
=>
lang
.
split
(
"
-
"
)[
0
]));
if
(
!
currentLang
&&
preferredLanguages
.
length
===
0
)
{
return
{
message
:
"
No language information available.
"
,
};
}
const
linkTags
=
document
.
querySelectorAll
(
"
link[hreflang]
"
);
if
(
linkTags
.
length
===
0
)
{
return
{
current
:
currentLang
||
null
,
message
:
"
No <link> tags with hreflang available.
"
,
};
}
const
availableLanguages
=
[...
linkTags
].
map
((
link
)
=>
{
const
fullLang
=
link
.
hreflang
;
const
baseLang
=
fullLang
.
split
(
"
-
"
)[
0
];
let
label
=
link
.
getAttribute
(
'
data-monster-label
'
)
if
(
!
label
)
{
label
=
languages
?.[
fullLang
]
if
(
!
label
)
{
label
=
languages
?.[
baseLang
]
}
}
return
{
fullLang
,
baseLang
,
label
,
href
:
link
.
href
,
};
});
// filter availableLanguages to only include languages that are in the preferredLanguages array
const
offerableLanguages
=
availableLanguages
.
filter
(
lang
=>
preferredLanguages
.
includes
(
lang
.
fullLang
)
||
preferredLanguages
.
includes
(
lang
.
baseLang
));
if
(
offerableLanguages
.
length
===
0
)
{
return
{
current
:
currentLang
||
null
,
message
:
"
No available languages match the user's preferences.
"
,
available
:
availableLanguages
.
map
((
lang
)
=>
({
...
lang
,
weight
:
1
,
})),
};
}
// Helper function to determine the "weight" of a language match
function
getWeight
(
langEntry
)
{
// Full match has priority 3
if
(
preferredLanguages
.
includes
(
langEntry
.
fullLang
))
return
3
;
// Base language match has priority 2
if
(
preferredLanguages
.
includes
(
langEntry
.
baseLang
))
return
2
;
// No match is priority 1
return
1
;
}
// Sort the available languages by descending weight
offerableLanguages
.
sort
((
a
,
b
)
=>
getWeight
(
b
)
-
getWeight
(
a
));
// The best match is the first in the sorted list
const
bestMatch
=
offerableLanguages
[
0
];
const
bestMatchWeight
=
getWeight
(
bestMatch
);
const
currentLabel
=
languages
?.[
currentLang
]
||
currentLang
// If we found a language that matches user preferences (weight > 1)
if
(
bestMatchWeight
>
0
)
{
return
{
current
:
currentLang
||
null
,
currentLabel
:
currentLabel
,
preferred
:
{
full
:
bestMatch
.
fullLang
,
base
:
bestMatch
.
baseLang
,
label
:
bestMatch
.
label
,
href
:
bestMatch
.
href
,
},
available
:
availableLanguages
.
map
((
lang
)
=>
({
...
lang
,
weight
:
getWeight
(
lang
),
})),
offerable
:
offerableLanguages
.
map
((
lang
)
=>
({
...
lang
,
weight
:
getWeight
(
lang
),
})),
};
}
// If no language matched the user's preferences
return
{
current
:
currentLang
||
null
,
message
:
"
None of the preferred languages are available.
"
,
available
:
availableLanguages
.
map
((
lang
)
=>
({
...
lang
,
weight
:
getWeight
(
lang
),
})),
};
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment