mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-14 17:02:38 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f51cc89d2 | ||
|
|
bcdb4c3b78 | ||
|
|
e9b02d61e7 | ||
|
|
02419ffbd4 | ||
|
|
5db9631571 |
No files matched your search
+3
-2
@@ -115,10 +115,11 @@ function ping(target) {
|
||||
}
|
||||
|
||||
async function startServer() {
|
||||
// The version and whether this is a built app go to ui/frame_telemetry.py, which
|
||||
// sends nothing from a source checkout.
|
||||
// The version, the time zone and whether this is a built app go to ui/frame_telemetry.py,
|
||||
// which sends nothing from a source checkout.
|
||||
const env = { ...process.env, PATH: await loginPath(), FRAME_CONTROL_APP: "1",
|
||||
FRAME_CONTROL_VERSION: app.getVersion(), FRAME_CONTROL_LOG: LOG,
|
||||
FRAME_CONTROL_TIMEZONE: Intl.DateTimeFormat().resolvedOptions().timeZone || "",
|
||||
...(app.isPackaged ? { FRAME_CONTROL_PACKAGED: "1" } : {}),
|
||||
...(fs.existsSync(TOOLS) ? { FRAME_CONTROL_TOOLS: TOOLS } : {}) };
|
||||
python = await findPython(env);
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "frame-control",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frame-control",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"electron": "^44.4.5",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "frame-control",
|
||||
"productName": "Frame Control",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"description": "Desktop app for managing a Valve Steam Frame over SSH",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
|
||||
+10
-3
@@ -33,6 +33,13 @@ computer, exactly as they were sent.
|
||||
yours.
|
||||
- Every event includes the app version, OS name (macOS, Windows or Linux),
|
||||
CPU architecture and Python version.
|
||||
- `app_installed`, `app_updated` and `app_opened` carry `country`: a
|
||||
two-letter code such as `DE`, worked out on your computer from its time zone
|
||||
setting (`Europe/Berlin` becomes `DE`), so we can see roughly where Frame
|
||||
Control is used without knowing your address. The time zone itself isn't
|
||||
sent, and a setting that isn't a country's zone (such as UTC) sends nothing.
|
||||
Copies that showed the first-run notice before it mentioned this (before
|
||||
0.4.6) show it again, and don't send a country until it has been seen.
|
||||
- `frame_connected` carries `headset_id`: the first 16 hex digits of a
|
||||
SHA-256 hash of the headset's `/etc/machine-id`, worked out on the headset,
|
||||
so the machine-id itself never leaves it. It is the same for one headset on
|
||||
@@ -44,9 +51,9 @@ computer, exactly as they were sent.
|
||||
|
||||
| Event | When | Properties besides the common ones |
|
||||
|---|---|---|
|
||||
| `app_installed` | First run | |
|
||||
| `app_updated` | First run of a new version | `from_version` |
|
||||
| `app_opened` | At most once a day | |
|
||||
| `app_installed` | First run | `country` |
|
||||
| `app_updated` | First run of a new version | `from_version`, `country` |
|
||||
| `app_opened` | At most once a day | `country` |
|
||||
| `frame_connected` | The first time a SteamOS build is seen on each headset | `steamos_build`, `steamos_version`, `headset_id`, `headsets_saved` |
|
||||
| `tab_viewed` | The first click on each tab in a session | `tab` |
|
||||
| `install_finished` | Any install finishes, working or not | `kind` (apk, flatpak, steam, title, web), `ok`, `seconds`, `error_category` (only when `ok` is false), `installer_code`, `xr_layer_missing`, and see below |
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate ui/frame_timezones.py, the time zone -> country table usage statistics use.
|
||||
|
||||
From the IANA tz database's zone.tab (each zone's country) and tzdata.zi (the old
|
||||
names that link to them), as installed on macOS and most Linux systems. It prints the
|
||||
old names left out because their zone is shared by several countries: check any new
|
||||
one, and add it to OLD_SPELLINGS if it's just an old spelling of a zone.tab zone.
|
||||
|
||||
Run: python3 scripts/timezones.py [/usr/share/zoneinfo]
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
src = Path(sys.argv[1] if len(sys.argv) > 1 else '/usr/share/zoneinfo')
|
||||
country = {}
|
||||
for line in (src / 'zone.tab').read_text().splitlines():
|
||||
if line and not line.startswith('#'):
|
||||
cc, _, zone = line.split('\t')[:3]
|
||||
country[zone] = cc
|
||||
links = {}
|
||||
for line in (src / 'tzdata.zi').read_text().splitlines():
|
||||
if line.startswith('L '):
|
||||
_, target, alias = line.split()
|
||||
links[alias] = target
|
||||
def resolve(z):
|
||||
for _ in range(5):
|
||||
z = links.get(z, z)
|
||||
return z
|
||||
|
||||
|
||||
# An old name links to a zone with the same clocks, which can be in another country
|
||||
# (Africa/Asmera, Eritrea, links to Africa/Nairobi), so it only gets a country when every
|
||||
# zone.tab zone sharing that target is in one country.
|
||||
countries_of = {}
|
||||
for zone, cc in country.items():
|
||||
countries_of.setdefault(resolve(zone), set()).add(cc)
|
||||
# Clock rules rather than places (EET links to Athens, WET to Lisbon): no country.
|
||||
GENERIC = {'CET', 'EET', 'MET', 'WET', 'EST', 'MST', 'HST', 'CST6CDT', 'EST5EDT', 'MST7MDT', 'PST8PDT'}
|
||||
# Places that have left zone.tab, so their target's group looks like another country's.
|
||||
PLACES = {'Pacific/Johnston': 'UM'}
|
||||
dropped = []
|
||||
for alias in links:
|
||||
if alias in country or alias in GENERIC or alias.startswith('Etc/'):
|
||||
continue
|
||||
if alias in PLACES:
|
||||
country[alias] = PLACES[alias]
|
||||
continue
|
||||
ccs = countries_of.get(resolve(alias), set())
|
||||
if len(ccs) == 1:
|
||||
country[alias] = next(iter(ccs))
|
||||
else:
|
||||
dropped.append(alias)
|
||||
# Old names for a place in a zone.tab zone whose target is shared, checked by hand.
|
||||
OLD_SPELLINGS = {
|
||||
'Africa/Asmera': 'Africa/Asmara', 'Africa/Timbuktu': 'Africa/Bamako', 'America/Coral_Harbour': 'America/Atikokan',
|
||||
'America/Montreal': 'America/Toronto', 'America/Nipigon': 'America/Toronto', 'America/Thunder_Bay': 'America/Toronto',
|
||||
'Canada/Eastern': 'America/Toronto', 'America/Virgin': 'America/St_Thomas', 'Antarctica/South_Pole': 'Antarctica/McMurdo',
|
||||
'Asia/Rangoon': 'Asia/Yangon', 'Atlantic/Jan_Mayen': 'Arctic/Longyearbyen', 'Europe/Belfast': 'Europe/London',
|
||||
'GB': 'Europe/London', 'GB-Eire': 'Europe/London', 'Iceland': 'Atlantic/Reykjavik', 'NZ': 'Pacific/Auckland',
|
||||
'Pacific/Samoa': 'Pacific/Pago_Pago', 'US/Samoa': 'Pacific/Pago_Pago', 'Singapore': 'Asia/Singapore',
|
||||
'US/Arizona': 'America/Phoenix', 'Pacific/Truk': 'Pacific/Chuuk', 'Pacific/Ponape': 'Pacific/Pohnpei',
|
||||
'Pacific/Yap': 'Pacific/Chuuk'}
|
||||
for alias, zone in OLD_SPELLINGS.items():
|
||||
if alias in links and zone in country:
|
||||
country[alias] = country[zone]
|
||||
dropped.remove(alias)
|
||||
version = (src / '+VERSION').read_text().strip() if (src / '+VERSION').exists() else 'unknown'
|
||||
rows = ''.join(f' {z!r}: {c!r},\n' for z, c in sorted(country.items()))
|
||||
out = Path(__file__).resolve().parent.parent / 'ui' / 'frame_timezones.py'
|
||||
out.write_text(f'''"""Time zone name -> two-letter country, from the IANA tz database {version} (zone.tab, and
|
||||
the old names in tzdata.zi that link to its zones). Generated by scripts/timezones.py.
|
||||
Zones that aren't in one country, such as UTC, aren't here."""
|
||||
COUNTRY = {{
|
||||
{rows}}}
|
||||
''')
|
||||
print(f'{len(country)} zones, tz {version} -> {out}')
|
||||
print('no country (shared by several):', ' '.join(sorted(dropped)) or 'none')
|
||||
@@ -159,6 +159,42 @@ class Lifecycle(Base):
|
||||
e = self.queued()[-1]
|
||||
self.assertEqual((e["event"], e["properties"]["from_version"]), ("app_updated", "9.9.9"))
|
||||
|
||||
def test_opens_installs_and_updates_carry_the_country(self):
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": "Europe/Berlin"}):
|
||||
tm.app_started()
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_VERSION": "10.0.0"}):
|
||||
tm.app_started()
|
||||
self.assertEqual([(e["event"], e["properties"].get("country")) for e in self.queued()],
|
||||
[("app_installed", "DE"), ("app_opened", "DE"), ("app_updated", "DE")])
|
||||
self.assertNotIn("Europe/Berlin", tm.OUTBOX.read_text())
|
||||
|
||||
def test_only_zones_in_the_tz_table_give_a_country(self):
|
||||
for tz, want in (("America/Argentina/Buenos_Aires", "AR"), ("Asia/Calcutta", "IN"), ("US/Pacific", "US"),
|
||||
("Europe/Vaduz", "LI"), ("Africa/Asmera", "ER"), ("Africa/Asmara", "ER"), ("Pacific/Truk", "FM"),
|
||||
("Pacific/Ponape", "FM"), ("America/Montreal", "CA"), ("America/Nassau", "BS"),
|
||||
("Europe/Amsterdam", "NL"), ("Etc/GMT+10", None), ("CET", None),
|
||||
("EET", None), ("WET", None), ("PST8PDT", None), ("HST", None), ("Pacific/Johnston", "UM"),
|
||||
("Japan", "JP"), ("UTC", None), ("alice", None),
|
||||
("home/alex/private", None), ("America/alex", None), ("../etc/passwd", None)):
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": tz}):
|
||||
self.assertEqual(tm.country(), want, tz)
|
||||
|
||||
def test_country_from_tz_or_localtime_in_a_checkout(self):
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": "", "TZ": ":Asia/Tokyo"}):
|
||||
self.assertEqual(tm.country(), "JP")
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": "", "TZ": ""}):
|
||||
for target, want in (("/usr/share/zoneinfo/Europe/Paris", "FR"),
|
||||
("/var/db/timezone/zoneinfo/Australia/Sydney", "AU"),
|
||||
("/home/alex/zoneinfo/alex/private", None), ("/etc/mytime", None)):
|
||||
with mock.patch.object(tm.os.path, "realpath", return_value=target):
|
||||
self.assertEqual(tm.country(), want, target)
|
||||
|
||||
def test_a_new_user_who_opens_settings_first_still_gets_the_notice(self):
|
||||
tm.update_settings({"compat": True})
|
||||
self.assertFalse(tm.state()["noticeShown"])
|
||||
tm.update_settings({"noticeShown": True})
|
||||
self.assertTrue(tm.state()["noticeShown"])
|
||||
|
||||
def test_frame_build_once(self):
|
||||
tm.frame_seen("20260922.1", "3.8")
|
||||
tm.frame_seen("20260922.1", "3.8")
|
||||
@@ -230,6 +266,39 @@ class Sending(Base):
|
||||
self.assertEqual(self.queued(), [])
|
||||
self.assertEqual([e["event"] for e in tm.state()["sent"]], ["app_opened"])
|
||||
|
||||
def test_an_older_notice_still_sends(self):
|
||||
got = self.serve()
|
||||
tm._save({**tm._defaults(), "notice_shown": True, "notice_version": 1})
|
||||
tm.capture("app_opened")
|
||||
self.assertEqual(tm.flush(), 1)
|
||||
self.assertEqual(len(got), 1)
|
||||
|
||||
def test_an_older_notice_is_shown_again_and_holds_back_the_country(self):
|
||||
got = self.serve()
|
||||
tm.SETTINGS.write_text(json.dumps({"id": "x", "notice_shown": True, "installed_sent": True,
|
||||
"last_version": "9.9.9"})) # as 0.4.5 saved it
|
||||
self.assertFalse(tm.state()["noticeShown"])
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": "Europe/Berlin"}):
|
||||
tm.app_started()
|
||||
self.assertEqual(tm.flush(), 1) # the usual statistics still go
|
||||
tm.update_settings({"noticeShown": True})
|
||||
self.assertTrue(tm.state()["noticeShown"])
|
||||
tm._save({**tm.settings(), "last_open_day": None}) # the next day
|
||||
tm.app_started()
|
||||
self.assertEqual(tm.flush(), 1)
|
||||
self.assertEqual([b["batch"][0]["properties"].get("country") for _, b in got], [None, "DE"])
|
||||
|
||||
def test_turning_usage_on_still_waits_for_the_new_notice(self):
|
||||
got = self.serve()
|
||||
tm.SETTINGS.write_text(json.dumps({"id": "x", "usage": False, "notice_shown": True, "installed_sent": True,
|
||||
"last_version": "9.9.9"})) # 0.4.5, usage off
|
||||
tm.update_settings({"usage": True})
|
||||
self.assertFalse(tm.state()["noticeShown"]) # so the page shows it
|
||||
with mock.patch.dict(os.environ, {"FRAME_CONTROL_TIMEZONE": "Europe/Berlin"}):
|
||||
tm.app_started()
|
||||
self.assertEqual(tm.flush(), 1)
|
||||
self.assertNotIn("country", got[0][1]["batch"][0]["properties"])
|
||||
|
||||
def test_a_failed_send_keeps_the_events(self):
|
||||
self.serve(status=500)
|
||||
tm.update_settings({"noticeShown": True})
|
||||
|
||||
+41
-11
@@ -18,7 +18,9 @@ Report a problem dialog (frame_report.py) sends bug reports privately to the
|
||||
same project whatever is chosen here.
|
||||
|
||||
Events are identified by a random id made on first run, not by the person or
|
||||
computer, and sent without person profiles or GeoIP. Nothing is sent without a
|
||||
computer, and sent without person profiles or GeoIP. Opens, installs and updates
|
||||
carry a country instead, worked out on this computer from its time zone setting
|
||||
(frame_timezones.py), to see roughly where Frame Control is used. Nothing is sent without a
|
||||
project key (ui/telemetry.json or $FRAME_CONTROL_POSTHOG_KEY), from a source
|
||||
checkout unless $FRAME_CONTROL_TELEMETRY=1, or when $DO_NOT_TRACK=1 or
|
||||
$FRAME_CONTROL_TELEMETRY=0.
|
||||
@@ -43,6 +45,7 @@ from pathlib import Path
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
import frame_host
|
||||
import frame_timezones
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
STATE = frame_host.data_dir('telemetry')
|
||||
@@ -65,6 +68,7 @@ EXPECTED_CATEGORIES = ('frame_unreachable', 'frame_not_set_up', 'frame_auth')
|
||||
DEFAULT_HOST = 'https://us.i.posthog.com'
|
||||
|
||||
LEVELS = ('usage', 'compat', 'diagnostics')
|
||||
NOTICE_VERSION = 2 # raise when the notice lists something new: it's shown again (2: country)
|
||||
# Events the page may send through /api/telemetry, and the properties each may carry.
|
||||
PAGE_EVENTS = {'tab_viewed': {'tab'}, 'update_offered': {'to_version'},
|
||||
'update_started': {'to_version'}, 'update_failed': {'to_version', 'error_category'}}
|
||||
@@ -105,7 +109,7 @@ def blocked():
|
||||
|
||||
def _defaults():
|
||||
return {'id': str(uuid.uuid4()), 'usage': True, 'compat': False, 'diagnostics': False,
|
||||
'notice_shown': False, 'installed_sent': False, 'last_version': None, 'last_open_day': None,
|
||||
'notice_shown': False, 'notice_version': 0, 'installed_sent': False, 'last_version': None, 'last_open_day': None,
|
||||
'frames_seen': [], 'compat_sent': []}
|
||||
|
||||
|
||||
@@ -147,12 +151,12 @@ def update_settings(changes):
|
||||
under way finishes first, so nothing leaves after this returns."""
|
||||
with _send_lock, _lock:
|
||||
s = settings()
|
||||
if 'noticeShown' in changes:
|
||||
s['notice_shown'] = bool(changes['noticeShown']) or s['notice_shown']
|
||||
if changes.get('noticeShown'):
|
||||
s['notice_shown'], s['notice_version'] = True, NOTICE_VERSION
|
||||
for level in LEVELS:
|
||||
if level in changes:
|
||||
s[level] = bool(changes[level])
|
||||
s['notice_shown'] = True
|
||||
s['notice_shown'] = True # only the notice itself moves notice_version on
|
||||
_save(s)
|
||||
_drop_unwanted(s)
|
||||
if changes.get('compat'):
|
||||
@@ -161,10 +165,16 @@ def update_settings(changes):
|
||||
return state()
|
||||
|
||||
|
||||
def _notice_current(s):
|
||||
"""Whether the notice this person saw lists everything that's sent now. If not, the page
|
||||
shows it again, and what it added waits until it has been seen."""
|
||||
return s['notice_shown'] and s['notice_version'] >= NOTICE_VERSION
|
||||
|
||||
|
||||
def state():
|
||||
"""What the page shows: the choices, why sending is blocked, and what was sent."""
|
||||
s = settings()
|
||||
return {'usage': s['usage'], 'compat': s['compat'], 'noticeShown': s['notice_shown'],
|
||||
return {'usage': s['usage'], 'compat': s['compat'], 'noticeShown': _notice_current(s),
|
||||
'diagnostics': s['diagnostics'],
|
||||
'blocked': blocked(), 'id': s['id'], 'queued': len(_read_lines(OUTBOX)),
|
||||
'sent': list(reversed(_read_lines(SENT)))[:50]}
|
||||
@@ -372,20 +382,36 @@ def page_event(body):
|
||||
return {'queued': capture(name, props)}
|
||||
|
||||
|
||||
def country():
|
||||
"""The two-letter country of the computer's time zone setting, or None. Only names in the
|
||||
tz database's table count, so nothing else in the setting can be sent. The app passes the
|
||||
zone from Electron, which knows it on every OS; from a checkout it's read from /etc/localtime."""
|
||||
tz = os.environ.get('FRAME_CONTROL_TIMEZONE') or os.environ.get('TZ', '').lstrip(':')
|
||||
if not tz:
|
||||
try:
|
||||
tz = os.path.realpath('/etc/localtime').split('/zoneinfo/', 1)[1]
|
||||
except (OSError, IndexError):
|
||||
return None
|
||||
return frame_timezones.COUNTRY.get(tz)
|
||||
|
||||
|
||||
def app_started():
|
||||
"""Once per server start: first install, an update, and one open a day."""
|
||||
"""Once per server start: first install, an update, and one open a day, with the country
|
||||
(left out when sending if the notice this person saw came before it: flush)."""
|
||||
if blocked():
|
||||
return
|
||||
with _lock:
|
||||
s = settings()
|
||||
version, today = app_version(), time.strftime('%Y-%m-%d')
|
||||
cc = country()
|
||||
where = {'country': cc} if cc else {}
|
||||
if not s['installed_sent']:
|
||||
capture('app_installed')
|
||||
capture('app_installed', where)
|
||||
s['installed_sent'] = True
|
||||
elif s['last_version'] and s['last_version'] != version:
|
||||
capture('app_updated', {'from_version': s['last_version']})
|
||||
capture('app_updated', {'from_version': s['last_version'], **where})
|
||||
if s['last_open_day'] != today:
|
||||
capture('app_opened')
|
||||
capture('app_opened', where)
|
||||
s['last_open_day'] = today
|
||||
s['last_version'] = version
|
||||
_save(s)
|
||||
@@ -580,10 +606,14 @@ def flush(timeout=20):
|
||||
if blocked() or not settings()['notice_shown']:
|
||||
return 0
|
||||
with _lock:
|
||||
_drop_unwanted(settings())
|
||||
s = settings()
|
||||
_drop_unwanted(s)
|
||||
batch = _read_lines(OUTBOX)[:100]
|
||||
if not batch:
|
||||
return 0
|
||||
if not _notice_current(s): # what a newer notice added stays here until it's been seen
|
||||
for e in batch:
|
||||
e['properties'].pop('country', None)
|
||||
try:
|
||||
post(batch, timeout)
|
||||
except SendError:
|
||||
|
||||
@@ -0,0 +1,547 @@
|
||||
"""Time zone name -> two-letter country, from the IANA tz database 2026c (zone.tab, and
|
||||
the old names in tzdata.zi that link to its zones). Generated by scripts/timezones.py.
|
||||
Zones that aren't in one country, such as UTC, aren't here."""
|
||||
COUNTRY = {
|
||||
'Africa/Abidjan': 'CI',
|
||||
'Africa/Accra': 'GH',
|
||||
'Africa/Addis_Ababa': 'ET',
|
||||
'Africa/Algiers': 'DZ',
|
||||
'Africa/Asmara': 'ER',
|
||||
'Africa/Asmera': 'ER',
|
||||
'Africa/Bamako': 'ML',
|
||||
'Africa/Bangui': 'CF',
|
||||
'Africa/Banjul': 'GM',
|
||||
'Africa/Bissau': 'GW',
|
||||
'Africa/Blantyre': 'MW',
|
||||
'Africa/Brazzaville': 'CG',
|
||||
'Africa/Bujumbura': 'BI',
|
||||
'Africa/Cairo': 'EG',
|
||||
'Africa/Casablanca': 'MA',
|
||||
'Africa/Ceuta': 'ES',
|
||||
'Africa/Conakry': 'GN',
|
||||
'Africa/Dakar': 'SN',
|
||||
'Africa/Dar_es_Salaam': 'TZ',
|
||||
'Africa/Djibouti': 'DJ',
|
||||
'Africa/Douala': 'CM',
|
||||
'Africa/El_Aaiun': 'EH',
|
||||
'Africa/Freetown': 'SL',
|
||||
'Africa/Gaborone': 'BW',
|
||||
'Africa/Harare': 'ZW',
|
||||
'Africa/Johannesburg': 'ZA',
|
||||
'Africa/Juba': 'SS',
|
||||
'Africa/Kampala': 'UG',
|
||||
'Africa/Khartoum': 'SD',
|
||||
'Africa/Kigali': 'RW',
|
||||
'Africa/Kinshasa': 'CD',
|
||||
'Africa/Lagos': 'NG',
|
||||
'Africa/Libreville': 'GA',
|
||||
'Africa/Lome': 'TG',
|
||||
'Africa/Luanda': 'AO',
|
||||
'Africa/Lubumbashi': 'CD',
|
||||
'Africa/Lusaka': 'ZM',
|
||||
'Africa/Malabo': 'GQ',
|
||||
'Africa/Maputo': 'MZ',
|
||||
'Africa/Maseru': 'LS',
|
||||
'Africa/Mbabane': 'SZ',
|
||||
'Africa/Mogadishu': 'SO',
|
||||
'Africa/Monrovia': 'LR',
|
||||
'Africa/Nairobi': 'KE',
|
||||
'Africa/Ndjamena': 'TD',
|
||||
'Africa/Niamey': 'NE',
|
||||
'Africa/Nouakchott': 'MR',
|
||||
'Africa/Ouagadougou': 'BF',
|
||||
'Africa/Porto-Novo': 'BJ',
|
||||
'Africa/Sao_Tome': 'ST',
|
||||
'Africa/Timbuktu': 'ML',
|
||||
'Africa/Tripoli': 'LY',
|
||||
'Africa/Tunis': 'TN',
|
||||
'Africa/Windhoek': 'NA',
|
||||
'America/Adak': 'US',
|
||||
'America/Anchorage': 'US',
|
||||
'America/Anguilla': 'AI',
|
||||
'America/Antigua': 'AG',
|
||||
'America/Araguaina': 'BR',
|
||||
'America/Argentina/Buenos_Aires': 'AR',
|
||||
'America/Argentina/Catamarca': 'AR',
|
||||
'America/Argentina/ComodRivadavia': 'AR',
|
||||
'America/Argentina/Cordoba': 'AR',
|
||||
'America/Argentina/Jujuy': 'AR',
|
||||
'America/Argentina/La_Rioja': 'AR',
|
||||
'America/Argentina/Mendoza': 'AR',
|
||||
'America/Argentina/Rio_Gallegos': 'AR',
|
||||
'America/Argentina/Salta': 'AR',
|
||||
'America/Argentina/San_Juan': 'AR',
|
||||
'America/Argentina/San_Luis': 'AR',
|
||||
'America/Argentina/Tucuman': 'AR',
|
||||
'America/Argentina/Ushuaia': 'AR',
|
||||
'America/Aruba': 'AW',
|
||||
'America/Asuncion': 'PY',
|
||||
'America/Atikokan': 'CA',
|
||||
'America/Atka': 'US',
|
||||
'America/Bahia': 'BR',
|
||||
'America/Bahia_Banderas': 'MX',
|
||||
'America/Barbados': 'BB',
|
||||
'America/Belem': 'BR',
|
||||
'America/Belize': 'BZ',
|
||||
'America/Blanc-Sablon': 'CA',
|
||||
'America/Boa_Vista': 'BR',
|
||||
'America/Bogota': 'CO',
|
||||
'America/Boise': 'US',
|
||||
'America/Buenos_Aires': 'AR',
|
||||
'America/Cambridge_Bay': 'CA',
|
||||
'America/Campo_Grande': 'BR',
|
||||
'America/Cancun': 'MX',
|
||||
'America/Caracas': 'VE',
|
||||
'America/Catamarca': 'AR',
|
||||
'America/Cayenne': 'GF',
|
||||
'America/Cayman': 'KY',
|
||||
'America/Chicago': 'US',
|
||||
'America/Chihuahua': 'MX',
|
||||
'America/Ciudad_Juarez': 'MX',
|
||||
'America/Coral_Harbour': 'CA',
|
||||
'America/Cordoba': 'AR',
|
||||
'America/Costa_Rica': 'CR',
|
||||
'America/Coyhaique': 'CL',
|
||||
'America/Creston': 'CA',
|
||||
'America/Cuiaba': 'BR',
|
||||
'America/Curacao': 'CW',
|
||||
'America/Danmarkshavn': 'GL',
|
||||
'America/Dawson': 'CA',
|
||||
'America/Dawson_Creek': 'CA',
|
||||
'America/Denver': 'US',
|
||||
'America/Detroit': 'US',
|
||||
'America/Dominica': 'DM',
|
||||
'America/Edmonton': 'CA',
|
||||
'America/Eirunepe': 'BR',
|
||||
'America/El_Salvador': 'SV',
|
||||
'America/Ensenada': 'MX',
|
||||
'America/Fort_Nelson': 'CA',
|
||||
'America/Fort_Wayne': 'US',
|
||||
'America/Fortaleza': 'BR',
|
||||
'America/Glace_Bay': 'CA',
|
||||
'America/Godthab': 'GL',
|
||||
'America/Goose_Bay': 'CA',
|
||||
'America/Grand_Turk': 'TC',
|
||||
'America/Grenada': 'GD',
|
||||
'America/Guadeloupe': 'GP',
|
||||
'America/Guatemala': 'GT',
|
||||
'America/Guayaquil': 'EC',
|
||||
'America/Guyana': 'GY',
|
||||
'America/Halifax': 'CA',
|
||||
'America/Havana': 'CU',
|
||||
'America/Hermosillo': 'MX',
|
||||
'America/Indiana/Indianapolis': 'US',
|
||||
'America/Indiana/Knox': 'US',
|
||||
'America/Indiana/Marengo': 'US',
|
||||
'America/Indiana/Petersburg': 'US',
|
||||
'America/Indiana/Tell_City': 'US',
|
||||
'America/Indiana/Vevay': 'US',
|
||||
'America/Indiana/Vincennes': 'US',
|
||||
'America/Indiana/Winamac': 'US',
|
||||
'America/Indianapolis': 'US',
|
||||
'America/Inuvik': 'CA',
|
||||
'America/Iqaluit': 'CA',
|
||||
'America/Jamaica': 'JM',
|
||||
'America/Jujuy': 'AR',
|
||||
'America/Juneau': 'US',
|
||||
'America/Kentucky/Louisville': 'US',
|
||||
'America/Kentucky/Monticello': 'US',
|
||||
'America/Knox_IN': 'US',
|
||||
'America/Kralendijk': 'BQ',
|
||||
'America/La_Paz': 'BO',
|
||||
'America/Lima': 'PE',
|
||||
'America/Los_Angeles': 'US',
|
||||
'America/Louisville': 'US',
|
||||
'America/Lower_Princes': 'SX',
|
||||
'America/Maceio': 'BR',
|
||||
'America/Managua': 'NI',
|
||||
'America/Manaus': 'BR',
|
||||
'America/Marigot': 'MF',
|
||||
'America/Martinique': 'MQ',
|
||||
'America/Matamoros': 'MX',
|
||||
'America/Mazatlan': 'MX',
|
||||
'America/Mendoza': 'AR',
|
||||
'America/Menominee': 'US',
|
||||
'America/Merida': 'MX',
|
||||
'America/Metlakatla': 'US',
|
||||
'America/Mexico_City': 'MX',
|
||||
'America/Miquelon': 'PM',
|
||||
'America/Moncton': 'CA',
|
||||
'America/Monterrey': 'MX',
|
||||
'America/Montevideo': 'UY',
|
||||
'America/Montreal': 'CA',
|
||||
'America/Montserrat': 'MS',
|
||||
'America/Nassau': 'BS',
|
||||
'America/New_York': 'US',
|
||||
'America/Nipigon': 'CA',
|
||||
'America/Nome': 'US',
|
||||
'America/Noronha': 'BR',
|
||||
'America/North_Dakota/Beulah': 'US',
|
||||
'America/North_Dakota/Center': 'US',
|
||||
'America/North_Dakota/New_Salem': 'US',
|
||||
'America/Nuuk': 'GL',
|
||||
'America/Ojinaga': 'MX',
|
||||
'America/Panama': 'PA',
|
||||
'America/Pangnirtung': 'CA',
|
||||
'America/Paramaribo': 'SR',
|
||||
'America/Phoenix': 'US',
|
||||
'America/Port-au-Prince': 'HT',
|
||||
'America/Port_of_Spain': 'TT',
|
||||
'America/Porto_Acre': 'BR',
|
||||
'America/Porto_Velho': 'BR',
|
||||
'America/Puerto_Rico': 'PR',
|
||||
'America/Punta_Arenas': 'CL',
|
||||
'America/Rainy_River': 'CA',
|
||||
'America/Rankin_Inlet': 'CA',
|
||||
'America/Recife': 'BR',
|
||||
'America/Regina': 'CA',
|
||||
'America/Resolute': 'CA',
|
||||
'America/Rio_Branco': 'BR',
|
||||
'America/Rosario': 'AR',
|
||||
'America/Santa_Isabel': 'MX',
|
||||
'America/Santarem': 'BR',
|
||||
'America/Santiago': 'CL',
|
||||
'America/Santo_Domingo': 'DO',
|
||||
'America/Sao_Paulo': 'BR',
|
||||
'America/Scoresbysund': 'GL',
|
||||
'America/Shiprock': 'US',
|
||||
'America/Sitka': 'US',
|
||||
'America/St_Barthelemy': 'BL',
|
||||
'America/St_Johns': 'CA',
|
||||
'America/St_Kitts': 'KN',
|
||||
'America/St_Lucia': 'LC',
|
||||
'America/St_Thomas': 'VI',
|
||||
'America/St_Vincent': 'VC',
|
||||
'America/Swift_Current': 'CA',
|
||||
'America/Tegucigalpa': 'HN',
|
||||
'America/Thule': 'GL',
|
||||
'America/Thunder_Bay': 'CA',
|
||||
'America/Tijuana': 'MX',
|
||||
'America/Toronto': 'CA',
|
||||
'America/Tortola': 'VG',
|
||||
'America/Vancouver': 'CA',
|
||||
'America/Virgin': 'VI',
|
||||
'America/Whitehorse': 'CA',
|
||||
'America/Winnipeg': 'CA',
|
||||
'America/Yakutat': 'US',
|
||||
'America/Yellowknife': 'CA',
|
||||
'Antarctica/Casey': 'AQ',
|
||||
'Antarctica/Davis': 'AQ',
|
||||
'Antarctica/DumontDUrville': 'AQ',
|
||||
'Antarctica/Macquarie': 'AU',
|
||||
'Antarctica/Mawson': 'AQ',
|
||||
'Antarctica/McMurdo': 'AQ',
|
||||
'Antarctica/Palmer': 'AQ',
|
||||
'Antarctica/Rothera': 'AQ',
|
||||
'Antarctica/South_Pole': 'AQ',
|
||||
'Antarctica/Syowa': 'AQ',
|
||||
'Antarctica/Troll': 'AQ',
|
||||
'Antarctica/Vostok': 'AQ',
|
||||
'Arctic/Longyearbyen': 'SJ',
|
||||
'Asia/Aden': 'YE',
|
||||
'Asia/Almaty': 'KZ',
|
||||
'Asia/Amman': 'JO',
|
||||
'Asia/Anadyr': 'RU',
|
||||
'Asia/Aqtau': 'KZ',
|
||||
'Asia/Aqtobe': 'KZ',
|
||||
'Asia/Ashgabat': 'TM',
|
||||
'Asia/Ashkhabad': 'TM',
|
||||
'Asia/Atyrau': 'KZ',
|
||||
'Asia/Baghdad': 'IQ',
|
||||
'Asia/Bahrain': 'BH',
|
||||
'Asia/Baku': 'AZ',
|
||||
'Asia/Bangkok': 'TH',
|
||||
'Asia/Barnaul': 'RU',
|
||||
'Asia/Beirut': 'LB',
|
||||
'Asia/Bishkek': 'KG',
|
||||
'Asia/Brunei': 'BN',
|
||||
'Asia/Calcutta': 'IN',
|
||||
'Asia/Chita': 'RU',
|
||||
'Asia/Choibalsan': 'MN',
|
||||
'Asia/Chongqing': 'CN',
|
||||
'Asia/Chungking': 'CN',
|
||||
'Asia/Colombo': 'LK',
|
||||
'Asia/Dacca': 'BD',
|
||||
'Asia/Damascus': 'SY',
|
||||
'Asia/Dhaka': 'BD',
|
||||
'Asia/Dili': 'TL',
|
||||
'Asia/Dubai': 'AE',
|
||||
'Asia/Dushanbe': 'TJ',
|
||||
'Asia/Famagusta': 'CY',
|
||||
'Asia/Gaza': 'PS',
|
||||
'Asia/Harbin': 'CN',
|
||||
'Asia/Hebron': 'PS',
|
||||
'Asia/Ho_Chi_Minh': 'VN',
|
||||
'Asia/Hong_Kong': 'HK',
|
||||
'Asia/Hovd': 'MN',
|
||||
'Asia/Irkutsk': 'RU',
|
||||
'Asia/Istanbul': 'TR',
|
||||
'Asia/Jakarta': 'ID',
|
||||
'Asia/Jayapura': 'ID',
|
||||
'Asia/Jerusalem': 'IL',
|
||||
'Asia/Kabul': 'AF',
|
||||
'Asia/Kamchatka': 'RU',
|
||||
'Asia/Karachi': 'PK',
|
||||
'Asia/Kashgar': 'CN',
|
||||
'Asia/Kathmandu': 'NP',
|
||||
'Asia/Katmandu': 'NP',
|
||||
'Asia/Khandyga': 'RU',
|
||||
'Asia/Kolkata': 'IN',
|
||||
'Asia/Krasnoyarsk': 'RU',
|
||||
'Asia/Kuala_Lumpur': 'MY',
|
||||
'Asia/Kuching': 'MY',
|
||||
'Asia/Kuwait': 'KW',
|
||||
'Asia/Macao': 'MO',
|
||||
'Asia/Macau': 'MO',
|
||||
'Asia/Magadan': 'RU',
|
||||
'Asia/Makassar': 'ID',
|
||||
'Asia/Manila': 'PH',
|
||||
'Asia/Muscat': 'OM',
|
||||
'Asia/Nicosia': 'CY',
|
||||
'Asia/Novokuznetsk': 'RU',
|
||||
'Asia/Novosibirsk': 'RU',
|
||||
'Asia/Omsk': 'RU',
|
||||
'Asia/Oral': 'KZ',
|
||||
'Asia/Phnom_Penh': 'KH',
|
||||
'Asia/Pontianak': 'ID',
|
||||
'Asia/Pyongyang': 'KP',
|
||||
'Asia/Qatar': 'QA',
|
||||
'Asia/Qostanay': 'KZ',
|
||||
'Asia/Qyzylorda': 'KZ',
|
||||
'Asia/Rangoon': 'MM',
|
||||
'Asia/Riyadh': 'SA',
|
||||
'Asia/Saigon': 'VN',
|
||||
'Asia/Sakhalin': 'RU',
|
||||
'Asia/Samarkand': 'UZ',
|
||||
'Asia/Seoul': 'KR',
|
||||
'Asia/Shanghai': 'CN',
|
||||
'Asia/Singapore': 'SG',
|
||||
'Asia/Srednekolymsk': 'RU',
|
||||
'Asia/Taipei': 'TW',
|
||||
'Asia/Tashkent': 'UZ',
|
||||
'Asia/Tbilisi': 'GE',
|
||||
'Asia/Tehran': 'IR',
|
||||
'Asia/Tel_Aviv': 'IL',
|
||||
'Asia/Thimbu': 'BT',
|
||||
'Asia/Thimphu': 'BT',
|
||||
'Asia/Tokyo': 'JP',
|
||||
'Asia/Tomsk': 'RU',
|
||||
'Asia/Ujung_Pandang': 'ID',
|
||||
'Asia/Ulaanbaatar': 'MN',
|
||||
'Asia/Ulan_Bator': 'MN',
|
||||
'Asia/Urumqi': 'CN',
|
||||
'Asia/Ust-Nera': 'RU',
|
||||
'Asia/Vientiane': 'LA',
|
||||
'Asia/Vladivostok': 'RU',
|
||||
'Asia/Yakutsk': 'RU',
|
||||
'Asia/Yangon': 'MM',
|
||||
'Asia/Yekaterinburg': 'RU',
|
||||
'Asia/Yerevan': 'AM',
|
||||
'Atlantic/Azores': 'PT',
|
||||
'Atlantic/Bermuda': 'BM',
|
||||
'Atlantic/Canary': 'ES',
|
||||
'Atlantic/Cape_Verde': 'CV',
|
||||
'Atlantic/Faeroe': 'FO',
|
||||
'Atlantic/Faroe': 'FO',
|
||||
'Atlantic/Jan_Mayen': 'SJ',
|
||||
'Atlantic/Madeira': 'PT',
|
||||
'Atlantic/Reykjavik': 'IS',
|
||||
'Atlantic/South_Georgia': 'GS',
|
||||
'Atlantic/St_Helena': 'SH',
|
||||
'Atlantic/Stanley': 'FK',
|
||||
'Australia/ACT': 'AU',
|
||||
'Australia/Adelaide': 'AU',
|
||||
'Australia/Brisbane': 'AU',
|
||||
'Australia/Broken_Hill': 'AU',
|
||||
'Australia/Canberra': 'AU',
|
||||
'Australia/Currie': 'AU',
|
||||
'Australia/Darwin': 'AU',
|
||||
'Australia/Eucla': 'AU',
|
||||
'Australia/Hobart': 'AU',
|
||||
'Australia/LHI': 'AU',
|
||||
'Australia/Lindeman': 'AU',
|
||||
'Australia/Lord_Howe': 'AU',
|
||||
'Australia/Melbourne': 'AU',
|
||||
'Australia/NSW': 'AU',
|
||||
'Australia/North': 'AU',
|
||||
'Australia/Perth': 'AU',
|
||||
'Australia/Queensland': 'AU',
|
||||
'Australia/South': 'AU',
|
||||
'Australia/Sydney': 'AU',
|
||||
'Australia/Tasmania': 'AU',
|
||||
'Australia/Victoria': 'AU',
|
||||
'Australia/West': 'AU',
|
||||
'Australia/Yancowinna': 'AU',
|
||||
'Brazil/Acre': 'BR',
|
||||
'Brazil/DeNoronha': 'BR',
|
||||
'Brazil/East': 'BR',
|
||||
'Brazil/West': 'BR',
|
||||
'Canada/Atlantic': 'CA',
|
||||
'Canada/Central': 'CA',
|
||||
'Canada/Eastern': 'CA',
|
||||
'Canada/Mountain': 'CA',
|
||||
'Canada/Newfoundland': 'CA',
|
||||
'Canada/Pacific': 'CA',
|
||||
'Canada/Saskatchewan': 'CA',
|
||||
'Canada/Yukon': 'CA',
|
||||
'Chile/Continental': 'CL',
|
||||
'Chile/EasterIsland': 'CL',
|
||||
'Cuba': 'CU',
|
||||
'Egypt': 'EG',
|
||||
'Eire': 'IE',
|
||||
'Europe/Amsterdam': 'NL',
|
||||
'Europe/Andorra': 'AD',
|
||||
'Europe/Astrakhan': 'RU',
|
||||
'Europe/Athens': 'GR',
|
||||
'Europe/Belfast': 'GB',
|
||||
'Europe/Belgrade': 'RS',
|
||||
'Europe/Berlin': 'DE',
|
||||
'Europe/Bratislava': 'SK',
|
||||
'Europe/Brussels': 'BE',
|
||||
'Europe/Bucharest': 'RO',
|
||||
'Europe/Budapest': 'HU',
|
||||
'Europe/Busingen': 'DE',
|
||||
'Europe/Chisinau': 'MD',
|
||||
'Europe/Copenhagen': 'DK',
|
||||
'Europe/Dublin': 'IE',
|
||||
'Europe/Gibraltar': 'GI',
|
||||
'Europe/Guernsey': 'GG',
|
||||
'Europe/Helsinki': 'FI',
|
||||
'Europe/Isle_of_Man': 'IM',
|
||||
'Europe/Istanbul': 'TR',
|
||||
'Europe/Jersey': 'JE',
|
||||
'Europe/Kaliningrad': 'RU',
|
||||
'Europe/Kiev': 'UA',
|
||||
'Europe/Kirov': 'RU',
|
||||
'Europe/Kyiv': 'UA',
|
||||
'Europe/Lisbon': 'PT',
|
||||
'Europe/Ljubljana': 'SI',
|
||||
'Europe/London': 'GB',
|
||||
'Europe/Luxembourg': 'LU',
|
||||
'Europe/Madrid': 'ES',
|
||||
'Europe/Malta': 'MT',
|
||||
'Europe/Mariehamn': 'AX',
|
||||
'Europe/Minsk': 'BY',
|
||||
'Europe/Monaco': 'MC',
|
||||
'Europe/Moscow': 'RU',
|
||||
'Europe/Nicosia': 'CY',
|
||||
'Europe/Oslo': 'NO',
|
||||
'Europe/Paris': 'FR',
|
||||
'Europe/Podgorica': 'ME',
|
||||
'Europe/Prague': 'CZ',
|
||||
'Europe/Riga': 'LV',
|
||||
'Europe/Rome': 'IT',
|
||||
'Europe/Samara': 'RU',
|
||||
'Europe/San_Marino': 'SM',
|
||||
'Europe/Sarajevo': 'BA',
|
||||
'Europe/Saratov': 'RU',
|
||||
'Europe/Simferopol': 'UA',
|
||||
'Europe/Skopje': 'MK',
|
||||
'Europe/Sofia': 'BG',
|
||||
'Europe/Stockholm': 'SE',
|
||||
'Europe/Tallinn': 'EE',
|
||||
'Europe/Tirane': 'AL',
|
||||
'Europe/Tiraspol': 'MD',
|
||||
'Europe/Ulyanovsk': 'RU',
|
||||
'Europe/Uzhgorod': 'UA',
|
||||
'Europe/Vaduz': 'LI',
|
||||
'Europe/Vatican': 'VA',
|
||||
'Europe/Vienna': 'AT',
|
||||
'Europe/Vilnius': 'LT',
|
||||
'Europe/Volgograd': 'RU',
|
||||
'Europe/Warsaw': 'PL',
|
||||
'Europe/Zagreb': 'HR',
|
||||
'Europe/Zaporozhye': 'UA',
|
||||
'Europe/Zurich': 'CH',
|
||||
'GB': 'GB',
|
||||
'GB-Eire': 'GB',
|
||||
'Hongkong': 'HK',
|
||||
'Iceland': 'IS',
|
||||
'Indian/Antananarivo': 'MG',
|
||||
'Indian/Chagos': 'IO',
|
||||
'Indian/Christmas': 'CX',
|
||||
'Indian/Cocos': 'CC',
|
||||
'Indian/Comoro': 'KM',
|
||||
'Indian/Kerguelen': 'TF',
|
||||
'Indian/Mahe': 'SC',
|
||||
'Indian/Maldives': 'MV',
|
||||
'Indian/Mauritius': 'MU',
|
||||
'Indian/Mayotte': 'YT',
|
||||
'Indian/Reunion': 'RE',
|
||||
'Iran': 'IR',
|
||||
'Israel': 'IL',
|
||||
'Jamaica': 'JM',
|
||||
'Japan': 'JP',
|
||||
'Kwajalein': 'MH',
|
||||
'Libya': 'LY',
|
||||
'Mexico/BajaNorte': 'MX',
|
||||
'Mexico/BajaSur': 'MX',
|
||||
'Mexico/General': 'MX',
|
||||
'NZ': 'NZ',
|
||||
'NZ-CHAT': 'NZ',
|
||||
'Navajo': 'US',
|
||||
'PRC': 'CN',
|
||||
'Pacific/Apia': 'WS',
|
||||
'Pacific/Auckland': 'NZ',
|
||||
'Pacific/Bougainville': 'PG',
|
||||
'Pacific/Chatham': 'NZ',
|
||||
'Pacific/Chuuk': 'FM',
|
||||
'Pacific/Easter': 'CL',
|
||||
'Pacific/Efate': 'VU',
|
||||
'Pacific/Enderbury': 'KI',
|
||||
'Pacific/Fakaofo': 'TK',
|
||||
'Pacific/Fiji': 'FJ',
|
||||
'Pacific/Funafuti': 'TV',
|
||||
'Pacific/Galapagos': 'EC',
|
||||
'Pacific/Gambier': 'PF',
|
||||
'Pacific/Guadalcanal': 'SB',
|
||||
'Pacific/Guam': 'GU',
|
||||
'Pacific/Honolulu': 'US',
|
||||
'Pacific/Johnston': 'UM',
|
||||
'Pacific/Kanton': 'KI',
|
||||
'Pacific/Kiritimati': 'KI',
|
||||
'Pacific/Kosrae': 'FM',
|
||||
'Pacific/Kwajalein': 'MH',
|
||||
'Pacific/Majuro': 'MH',
|
||||
'Pacific/Marquesas': 'PF',
|
||||
'Pacific/Midway': 'UM',
|
||||
'Pacific/Nauru': 'NR',
|
||||
'Pacific/Niue': 'NU',
|
||||
'Pacific/Norfolk': 'NF',
|
||||
'Pacific/Noumea': 'NC',
|
||||
'Pacific/Pago_Pago': 'AS',
|
||||
'Pacific/Palau': 'PW',
|
||||
'Pacific/Pitcairn': 'PN',
|
||||
'Pacific/Pohnpei': 'FM',
|
||||
'Pacific/Ponape': 'FM',
|
||||
'Pacific/Port_Moresby': 'PG',
|
||||
'Pacific/Rarotonga': 'CK',
|
||||
'Pacific/Saipan': 'MP',
|
||||
'Pacific/Samoa': 'AS',
|
||||
'Pacific/Tahiti': 'PF',
|
||||
'Pacific/Tarawa': 'KI',
|
||||
'Pacific/Tongatapu': 'TO',
|
||||
'Pacific/Truk': 'FM',
|
||||
'Pacific/Wake': 'UM',
|
||||
'Pacific/Wallis': 'WF',
|
||||
'Pacific/Yap': 'FM',
|
||||
'Poland': 'PL',
|
||||
'Portugal': 'PT',
|
||||
'ROC': 'TW',
|
||||
'ROK': 'KR',
|
||||
'Singapore': 'SG',
|
||||
'Turkey': 'TR',
|
||||
'US/Alaska': 'US',
|
||||
'US/Aleutian': 'US',
|
||||
'US/Arizona': 'US',
|
||||
'US/Central': 'US',
|
||||
'US/East-Indiana': 'US',
|
||||
'US/Eastern': 'US',
|
||||
'US/Hawaii': 'US',
|
||||
'US/Indiana-Starke': 'US',
|
||||
'US/Michigan': 'US',
|
||||
'US/Mountain': 'US',
|
||||
'US/Pacific': 'US',
|
||||
'US/Samoa': 'AS',
|
||||
'W-SU': 'RU',
|
||||
}
|
||||
+2
-2
@@ -683,7 +683,7 @@
|
||||
</div>
|
||||
<div class="notice" id="privacyNotice" hidden>
|
||||
<div class="grow">Frame Control sends anonymous usage statistics: that it was installed and opened, its version,
|
||||
your operating system, which tabs you use, whether installs on the Frame worked, and how many headsets you use
|
||||
your operating system, your country (from the computer's time zone setting), which tabs you use, whether installs on the Frame worked, and how many headsets you use
|
||||
(a one-way hash of each headset's machine ID, the same on every computer, so they're counted once). Never file names, paths,
|
||||
addresses or anything you've typed, and it isn't linked to you. You can also share whether Android apps
|
||||
worked and the details of errors, which helps fix problems faster.</div>
|
||||
@@ -1148,7 +1148,7 @@
|
||||
<section class="panel" id="privacy">
|
||||
<div class="shelf-head"><h2>Privacy & updates</h2><span class="spacer"></span><span class="sub" id="appVersion"></span></div>
|
||||
<label class="popt"><input type="checkbox" id="tUsage"><b>Anonymous usage statistics</b>
|
||||
<span class="sub">Installs, opens, version, operating system, tabs used, and whether installs on the Frame
|
||||
<span class="sub">Installs, opens, version, operating system, country (from the time zone setting, to see roughly where it's used), tabs used, and whether installs on the Frame
|
||||
worked (with an error category, never the message). F-Droid package names only. Each headset's SteamOS
|
||||
build, a one-way hash of its machine ID (to count headsets, not just installs), and how many you've set up.</span></label>
|
||||
<label class="popt"><input type="checkbox" id="tCompat"><b>Share compatibility results</b>
|
||||
|
||||
Reference in new issue
Block a user