Skip to content

Commit 04da737

Browse files
feat: detect and use browser language as default for first-time users
1 parent 8cdba71 commit 04da737

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

client/i18n.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
enIN
2222
} from 'date-fns/locale';
2323

24+
import getPreferredLanguage from './utils/language-utils';
25+
2426
const fallbackLng = ['en-US'];
2527

2628
export const availableLanguages = [
@@ -42,6 +44,21 @@ export const availableLanguages = [
4244
'ur'
4345
];
4446

47+
const detectedLanguage = getPreferredLanguage(
48+
availableLanguages,
49+
fallbackLng[0]
50+
);
51+
52+
let initialLanguage = detectedLanguage;
53+
54+
// if user has a saved preference (e.g., from redux or window.__INITIAL_STATE__), use that
55+
if (
56+
window.__INITIAL_STATE__?.preferences?.language &&
57+
availableLanguages.includes(window.__INITIAL_STATE__.preferences.language)
58+
) {
59+
initialLanguage = window.__INITIAL_STATE__.preferences.language;
60+
}
61+
4562
export function languageKeyToLabel(lang) {
4663
const languageMap = {
4764
be: 'বাংলা',
@@ -104,7 +121,7 @@ i18n
104121
// .use(LanguageDetector)// to detect the language from currentBrowser
105122
.use(Backend) // to fetch the data from server
106123
.init({
107-
lng: 'en-US',
124+
lng: initialLanguage,
108125
fallbackLng, // if user computer language is not on the list of available languages, than we will be using the fallback language specified earlier
109126
debug: false,
110127
backend: options,

client/modules/IDE/reducers/preferences.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as ActionTypes from '../../../constants';
2+
import i18n from '../../../i18n';
23

34
export const initialState = {
45
tabIndex: 0,
@@ -11,7 +12,7 @@ export const initialState = {
1112
gridOutput: false,
1213
theme: 'light',
1314
autorefresh: false,
14-
language: 'en-US',
15+
language: i18n.language,
1516
autocloseBracketsQuotes: true,
1617
autocompleteHinter: false
1718
};

client/utils/language-utils.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Utility functions for language detection and handling
3+
*/
4+
5+
function detectLanguageFromUserAgent(userAgent) {
6+
const langRegexes = [
7+
/\b([a-z]{2}(-[A-Z]{2})?);/i, // matches patterns like "en;" or "en-US;"
8+
/\[([a-z]{2}(-[A-Z]{2})?)\]/i // matches patterns like "[en]" or "[en-US]"
9+
];
10+
11+
const match = langRegexes.reduce((result, regex) => {
12+
if (result) return result;
13+
const matches = userAgent.match(regex);
14+
return matches && matches[1] ? matches[1] : null;
15+
}, null);
16+
17+
return match;
18+
}
19+
20+
function getPreferredLanguage(supportedLanguages = [], defaultLanguage = 'en') {
21+
if (typeof navigator === 'undefined') {
22+
return defaultLanguage;
23+
}
24+
25+
const normalizeLanguage = (langCode) => langCode.toLowerCase().trim();
26+
27+
const normalizedSupported = supportedLanguages.map(normalizeLanguage);
28+
29+
if (navigator.languages && navigator.languages.length) {
30+
const matchedLang = navigator.languages.find((browserLang) => {
31+
const normalizedBrowserLang = normalizeLanguage(browserLang);
32+
33+
const hasExactMatch =
34+
normalizedSupported.findIndex(
35+
(lang) => lang === normalizedBrowserLang
36+
) !== -1;
37+
38+
if (hasExactMatch) {
39+
return true;
40+
}
41+
42+
const languageOnly = normalizedBrowserLang.split('-')[0];
43+
const hasLanguageOnlyMatch =
44+
normalizedSupported.findIndex(
45+
(lang) => lang === languageOnly || lang.startsWith(`${languageOnly}-`)
46+
) !== -1;
47+
48+
return hasLanguageOnlyMatch;
49+
});
50+
51+
if (matchedLang) {
52+
const normalizedMatchedLang = normalizeLanguage(matchedLang);
53+
const exactMatchIndex = normalizedSupported.findIndex(
54+
(lang) => lang === normalizedMatchedLang
55+
);
56+
57+
if (exactMatchIndex !== -1) {
58+
return supportedLanguages[exactMatchIndex];
59+
}
60+
61+
const languageOnly = normalizedMatchedLang.split('-')[0];
62+
const languageOnlyMatchIndex = normalizedSupported.findIndex(
63+
(lang) => lang === languageOnly || lang.startsWith(`${languageOnly}-`)
64+
);
65+
66+
if (languageOnlyMatchIndex !== -1) {
67+
return supportedLanguages[languageOnlyMatchIndex];
68+
}
69+
}
70+
}
71+
72+
if (navigator.language) {
73+
const normalizedNavLang = normalizeLanguage(navigator.language);
74+
const exactMatchIndex = normalizedSupported.findIndex(
75+
(lang) => lang === normalizedNavLang
76+
);
77+
78+
if (exactMatchIndex !== -1) {
79+
return supportedLanguages[exactMatchIndex];
80+
}
81+
82+
const languageOnly = normalizedNavLang.split('-')[0];
83+
const languageOnlyMatchIndex = normalizedSupported.findIndex(
84+
(lang) => lang === languageOnly || lang.startsWith(`${languageOnly}-`)
85+
);
86+
87+
if (languageOnlyMatchIndex !== -1) {
88+
return supportedLanguages[languageOnlyMatchIndex];
89+
}
90+
}
91+
92+
if (navigator.userAgent) {
93+
const userAgentLang = detectLanguageFromUserAgent(navigator.userAgent);
94+
if (
95+
userAgentLang &&
96+
normalizedSupported.includes(normalizeLanguage(userAgentLang))
97+
) {
98+
const index = normalizedSupported.indexOf(
99+
normalizeLanguage(userAgentLang)
100+
);
101+
return supportedLanguages[index];
102+
}
103+
}
104+
return defaultLanguage;
105+
}
106+
107+
export default getPreferredLanguage;

0 commit comments

Comments
 (0)