Skip to content

Commit 6b2e349

Browse files
authored
Merge pull request #432 from KartikTalwar/detect_localization
Try to improve locale-detection.
2 parents 45ff062 + 1832400 commit 6b2e349

File tree

3 files changed

+207
-12
lines changed

3 files changed

+207
-12
lines changed

src/gmail.js

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,29 +142,98 @@ var Gmail_ = function(localJQuery) {
142142
return false;
143143
}
144144

145+
if (locale.match(/[0-9]/)) {
146+
return false;
147+
}
148+
145149
var localePrefix = locale.slice(0, 2);
146150
return localePrefix.toLowerCase() === localePrefix ||
147151
localePrefix.toUpperCase() === localePrefix;
148152
};
149153

150-
api.get.localization = function() {
151-
var globals = api.tracker.globals;
154+
api.helper.filter_locale = function(locale) {
155+
if (!api.helper.get.is_locale(locale)) {
156+
return null;
157+
}
152158

153-
// First candidate.
154-
var locale = globals[17] && globals[17][8] && globals[17][8][8];
155-
if (api.helper.get.is_locale(locale)) {
156-
return locale.toLowerCase();
159+
// strip region-denominator
160+
return locale.substring(0,2).toLowerCase();
161+
};
162+
163+
api.helper.array_starts_with = function(list, item) {
164+
if (list && list.length > 0 && list[0] === item) {
165+
return true;
166+
} else {
167+
return false;
157168
}
169+
};
158170

159-
// Second candidate.
160-
locale = globals[17] && globals[17][9] && globals[17][9][8];
161-
if (api.helper.get.is_locale(locale)) {
162-
return locale.toLowerCase();
171+
api.helper.get.array_sublist = function(nestedArray, itemKey) {
172+
if (nestedArray) {
173+
for(var i=0; i<nestedArray.length; i++) {
174+
var list = nestedArray[i];
175+
if (api.helper.array_starts_with(list, itemKey)) {
176+
return list;
177+
}
178+
}
163179
}
164180

165181
return null;
166182
};
167183

184+
api.helper.get.locale_from_url_params = function(value) {
185+
// check if is URL
186+
if (value && value.indexOf && value.indexOf("https://") === 0) {
187+
var urlParts = value.split("?");
188+
if (urlParts.length > 1) {
189+
var hash = urlParts[1];
190+
var hashParts = hash.split("&");
191+
for (var i=0; i < hashParts.length; i++)
192+
{
193+
var kvp = hashParts[i].split("=");
194+
if (kvp.length === 2 && kvp[0] === "hl") {
195+
return kvp[1];
196+
}
197+
}
198+
}
199+
}
200+
201+
return null;
202+
};
203+
204+
api.helper.get.locale_from_globals_item = function(list) {
205+
if (!list) {
206+
return null;
207+
}
208+
209+
for (var i=0; i<list.length; i++) {
210+
var item = list[i];
211+
var locale = api.helper.get.locale_from_url_params(item);
212+
if (locale) {
213+
return locale;
214+
}
215+
}
216+
217+
// fallback to user-locale
218+
return list[8];
219+
};
220+
221+
api.get.localization = function() {
222+
var globals = api.tracker.globals;
223+
224+
// candidate is globals[17]-subarray which starts with "ui"
225+
// has historically been observed as [7], [8] and [9]!
226+
var localeList = api.helper.get.array_sublist(globals[17], "ui");
227+
if (localeList !== null && localeList.length > 8) {
228+
var locale = api.helper.get.locale_from_globals_item(localeList);
229+
locale = api.helper.filter_locale(locale);
230+
if (locale) {
231+
return locale;
232+
}
233+
}
234+
235+
return null;
236+
};
168237

169238
api.check.is_thread = function() {
170239
var check_1 = $(".nH .if").children(":eq(1)").children().children(":eq(1)").children();

test/test.locale.js

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"use strict";
22
let assert = require('assert');
33
let Gmail = require('../src/gmail').Gmail;
4+
let gmail = new Gmail();
45

56
describe("Locale-parsing", () => {
67
it("Recognizes consistently cased locales", () => {
7-
let gmail = new Gmail();
88
let locales = ["EN","NO", "en", "no"];
99

1010
locales.forEach((locale) => {
@@ -14,12 +14,90 @@ describe("Locale-parsing", () => {
1414
});
1515

1616
it("Rejects inconsistently cased locales", () => {
17-
let gmail = new Gmail();
1817
let locales = ["En","No", "eN", "nO"];
1918

2019
locales.forEach((locale) => {
2120
let result = gmail.helper.get.is_locale(locale);
2221
assert.ok(!result);
2322
});
2423
});
24+
25+
it("Rejects badly formatted string", () => {
26+
let junk = ["12", "12-23", "a3", "a45b4"];
27+
junk.forEach((junkValue) => {
28+
let result = gmail.helper.get.is_locale(junkValue);
29+
assert.ok(!result);
30+
});
31+
});
32+
});
33+
34+
describe("Locale URL-parsing", () => {
35+
let testCase = function(url, expected) {
36+
const result = gmail.helper.get.locale_from_url_params(url);
37+
assert.equal(expected, result);
38+
};
39+
40+
it("returns null from empty or null", () => {
41+
testCase(null, null);
42+
testCase("", null);
43+
});
44+
45+
it("returns null from URLs with no language-code", () => {
46+
testCase("https://boo/hiss", null);
47+
testCase("https://account.google.com/user/stats?show=true", null);
48+
});
49+
50+
it("returns language-codes embedded in URLs", () => {
51+
testCase("https://account.google.com/user/stats?hl=en", "en");
52+
testCase("https://account.google.com/user/stats?firstParam=value&hl=en", "en");
53+
testCase("https://account.google.com/user/stats?foo=bar&hl=no&someMoreStuff", "no");
54+
});
55+
});
56+
57+
describe("Globals local-list parsing", () => {
58+
let testCase = function(listlist, expected) {
59+
const result = gmail.helper.get.locale_from_globals_item(listlist);
60+
assert.deepEqual(expected, result);
61+
};
62+
63+
it("returns null for null or empty list", () => {
64+
testCase(null, null);
65+
testCase([], null);
66+
});
67+
68+
it("returns null for no match", () => {
69+
testCase(["uiv"], null);
70+
});
71+
72+
it("returns ui[8] when no URLs match", () => {
73+
testCase(["ui", 1, 2, 3, 4, 5, 6, 7, "NO"], "NO");
74+
});
75+
76+
it("returns hl-value when URLs match", () => {
77+
testCase(["https://account.google.com/user/stats?firstParam=value&hl=en"], "en");
78+
});
79+
});
80+
81+
describe("Locale-filtering", () => {
82+
let testCase = function(locale, expected) {
83+
const result = gmail.helper.filter_locale(locale);
84+
assert.equal(expected, result);
85+
};
86+
87+
it("filters junk values", () => {
88+
testCase(null, null);
89+
testCase("", null);
90+
testCase("12-23", null);
91+
testCase("eN", null);
92+
});
93+
94+
it("returned value is always lower-case", () => {
95+
testCase("EN", "en");
96+
testCase("en", "en");
97+
});
98+
99+
it("returned value does not contain region-denomination", () => {
100+
testCase("EN-GB", "en");
101+
testCase("NO-NB", "no");
102+
});
25103
});

test/test.parsing.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,51 @@ describe("Name-parsing", () => {
130130
testName("Senõr Alapenõ on a stick");
131131
});
132132
});
133+
134+
describe("List-prefix checking", () => {
135+
const gmail = new Gmail();
136+
137+
const testCase = function(list, searchee, expected) {
138+
const result = gmail.helper.array_starts_with(list, searchee);
139+
assert.equal(expected, result);
140+
};
141+
142+
it("returns false for null or empty list", () => {
143+
testCase(null, "key", false);
144+
testCase([], "key", false);
145+
});
146+
147+
it("returns false for miss", () => {
148+
testCase(["ui", "yes"], "uiv", false);
149+
});
150+
151+
it("returns true for exact hit", () => {
152+
testCase(["ui", "yes"], "ui", true);
153+
});
154+
});
155+
156+
describe("Sub-list extraction", () => {
157+
const gmail = new Gmail();
158+
159+
const testCase = function(listlist, prefix, expected) {
160+
const result = gmail.helper.get.array_sublist(listlist, prefix);
161+
assert.deepEqual(expected, result);
162+
};
163+
164+
it("returns null for null or empty list", () => {
165+
testCase(null, "ui", null);
166+
testCase([], "ui", null);
167+
});
168+
169+
it("returns null for no match", () => {
170+
testCase([["uiv", "a"]], "ui", null);
171+
});
172+
173+
it("returns the full matching list on match", () => {
174+
testCase([
175+
["a", "b", "c"],
176+
["ui", "yeah"],
177+
["d", "e", "f"]
178+
], "ui", ["ui", "yeah"]);
179+
});
180+
});

0 commit comments

Comments
 (0)