blob: 34e2db343602c941fbd5106ff7559d317edbd17e [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Authoritative source for factory region/locale configuration.
Run this module to display all known regions (use --help to see options).
"""
import argparse
import collections
import sys
import factory_common # pylint: disable=W0611
from cros.factory.test.utils import Enum
KeyboardMechanicalLayout = Enum(['ANSI', 'ISO', 'JIS'])
class Region(object):
"""Comprehensive, standard locale configuration per country/region."""
# pylint gets confused by some of the docstrings.
# pylint: disable=C0322
region_code = None
"""A unique identifier for the region. This may be a lower-case
`ISO 3166-1 alpha-2 code
<http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2>`_ (e.g., ``us``),
a variant within an alpha-2 entity (e.g., ``ca.fr``), or an
identifier for a collection of countries or entities (e.g.,
``latam-es-419`` or ``nordic``). See :ref:`region-codes`.
Note that ``uk`` is not a valid identifier; ``gb`` is used as it is
the real alpha-2 code for the UK.
"""
keyboard = None
"""An XKB keyboard layout identifier (e.g., ``xkb:us:intl:eng``);
see `input_method_util.cc <http://goo.gl/3aJnl>`_ and
`input_methods.txt <http://goo.gl/xWNrUP>`_ for supported keyboards.
Note that the keyboard must be whitelisted for login, i.e.,
the respective line in `input_methods.txt <http://goo.gl/xWNrUP>`_ must
contain the ``login`` keyword.
This is used to set the VPD ``keyboard_layout`` value."""
time_zone = None
"""A `tz database time zone
<http://en.wikipedia.org/wiki/List_of_tz_database_time_zones>`_
identifier (e.g., ``America/Los_Angeles``). See
`timezone_settings.cc <http://goo.gl/WSVUeE>`_ for supported time
zones.
This is used to set the VPD ``initial_timezone`` value."""
language_code = None
"""The default language code (e.g., ``en-US``); see
`l10n_util.cc <http://goo.gl/kVkht>`_ for supported languages.
This is used to set the VPD ``initial_locale`` language."""
keyboard_mechanical_layout = None
"""The keyboard's mechanical layout (``ANSI`` [US-like], ``ISO``
[UK-like], or ``JIS``)."""
description = None
"""A human-readable description of the region.
This defaults to :py:attr:`region_code` if not set."""
notes = None
"""Notes about the region. This may be None."""
FIELDS = ['region_code', 'keyboard', 'time_zone', 'language_code',
'keyboard_mechanical_layout']
def __init__(self, region_code, keyboard, time_zone, language_code,
keyboard_mechanical_layout, description=None, notes=None):
# Quick check: should be 'gb', not 'uk'
assert region_code != 'uk'
self.region_code = region_code
self.keyboard = keyboard
self.time_zone = time_zone
self.language_code = language_code
self.keyboard_mechanical_layout = keyboard_mechanical_layout
self.description = description or region_code
self.notes = notes
def __repr__(self):
return 'Region(%s)' % (', '.join([getattr(self, x) for x in self.FIELDS]))
_KML = KeyboardMechanicalLayout
REGIONS_LIST = [
Region('au', 'xkb:us::eng', 'Australia/Sydney', 'en-AU', _KML.ANSI,
'Australia'),
Region('ca.ansi', 'xkb:us::eng', 'America/Toronto', 'en-CA', _KML.ANSI,
'Canada (US keyboard)',
'Canada with US (ANSI) keyboard; see http://goto/cros-canada'),
Region('ca.fr', 'xkb:ca::fra', 'America/Toronto', 'fr-CA', _KML.ISO,
'Canada (French keyboard)',
('Device shipping to Canada with a purely Canadian French (ISO) '
'keyboard; see http://goto/cros-canada')),
Region('ca.hybrid', 'xkb:ca:eng:eng', 'America/Toronto', 'en-CA', _KML.ISO,
'Canada (hybrid)',
('Canada with hybrid xkb:ca:eng:eng + xkb:ca::fra keyboard (ISO), '
'defaulting to English language and keyboard; see '
'http://goto/cros-canada')),
Region('ca.multix', 'xkb:ca:multix:fra', 'America/Toronto', 'fr-CA',
_KML.ISO, 'Canada (multilingual)',
('Device shipping to Canada with a Canadian Multilingual keyboard; '
'see http://goto/cros-canada')),
Region('de', 'xkb:de::ger', 'Europe/Berlin', 'de', _KML.ISO, 'Germany'),
Region('fi', 'xkb:fi::fin', 'Europe/Helsinki', 'fi', _KML.ISO, 'Finland'),
Region('fr', 'xkb:fr::fra', 'Europe/Paris', 'fr', _KML.ISO, 'France'),
Region('gb', 'xkb:gb:extd:eng', 'Europe/London', 'en-GB', _KML.ISO, 'UK'),
Region('ie', 'xkb:gb:extd:eng', 'Europe/Dublin', 'en-GB', _KML.ISO,
'Ireland'),
Region('in', 'xkb:us::eng', 'Asia/Calcutta', 'en-US', _KML.ANSI, 'India'),
Region('my', 'xkb:us::eng', 'Asia/Kuala_Lumpur', 'ms', _KML.ANSI,
'Malaysia'),
Region('nl', 'xkb:us:intl:eng', 'Europe/Amsterdam', 'nl', _KML.ANSI,
'Netherlands'),
Region('nordic', 'xkb:se::swe', 'Europe/Stockholm', 'en-US', _KML.ISO,
'Nordics', 'Unified keyboard for Sweden, Norway, and Denmark'),
Region('se', 'xkb:se::swe', 'Europe/Stockholm', 'sv', _KML.ISO, 'Sweden'),
Region('sg', 'xkb:us::eng', 'Asia/Singapore', 'en-GB', _KML.ANSI,
'Singapore'),
Region('us', 'xkb:us::eng', 'America/Los_Angeles', 'en-US', _KML.ANSI,
'United States'),
]
"""A list of :py:class:`cros.factory.l10n.regions.Region` objects for
all **confirmed** regions. A confirmed region is a region whose
properties are known to be correct and may be used to launch a device."""
UNCONFIRMED_REGIONS_LIST = []
"""A list of :py:class:`cros.factory.l10n.regions.Region` objects for
**unconfirmed** regions. These are believed to be correct but
unconfirmed, and all fields should be verified (and the row moved into
:py:data:`cros.factory.l10n.regions.Region.REGIONS_LIST`) before
launch. See <http://goto/vpdsettings>.
Currently, non-Latin keyboards must use an underlying Latin keyboard
for VPD. (This assumption should be revisited when moving items to
:py:data:`cros.factory.l10n.regions.Region.REGIONS_LIST`.) This is
currently being discussed on <http://crbug.com/325389>.
Some timezones may be missing from ``timezone_settings.cc`` (see
http://crosbug.com/p/23902). This must be rectified before moving
items to :py:data:`cros.factory.l10n.regions.Region.REGIONS_LIST`.
"""
INCOMPLETE_REGIONS_LIST = []
"""A list of :py:class:`cros.factory.l10n.regions.Region` objects for
**incomplete** regions. These may contain incorrect information, and all
fields must be reviewed before launch. See http://goto/vpdsettings.
"""
def BuildRegionsDict(include_all=False):
"""Builds a dictionary mapping region code to
:py:class:`py.l10n.regions.Region` object.
The regions include:
* :py:data:`cros.factory.l10n.regions.REGIONS_LIST`
* :py:data:`cros.factory.l10n.regions_overlay.REGIONS_LIST`
* Only if ``include_all`` is true:
* :py:data:`cros.factory.l10n.regions.UNCONFIRMED_REGIONS_LIST`
* :py:data:`cros.factory.l10n.regions_overlay.UNCONFIRMED_REGIONS_LIST`
* :py:data:`cros.factory.l10n.regions.INCOMPLETE_REGIONS_LIST`
* :py:data:`cros.factory.l10n.regions_overlay.INCOMPLETE_REGIONS_LIST`
A region may only appear in one of the above lists, or this function
will (deliberately) fail.
"""
regions = list(REGIONS_LIST)
if include_all:
regions += UNCONFIRMED_REGIONS_LIST + INCOMPLETE_REGIONS_LIST
try:
from cros.factory.l10n import regions_overlay # pylint: disable=E0611
# REGIONS_LIST must be present
regions += regions_overlay.REGIONS_LIST
if include_all:
for name in ('UNCONFIRMED_REGIONS_LIST', 'INCOMPLETE_REGIONS_LIST'):
regions += getattr(regions_overlay, name, [])
except ImportError:
pass
region_codes = [r.region_code for r in regions]
dups = sorted([
k for k, v in collections.Counter(region_codes).iteritems() if v > 1])
assert not dups, 'Duplicate region codes: %s' % dups
return dict((x.region_code, x) for x in regions)
REGIONS = BuildRegionsDict()
def main():
parser = argparse.ArgumentParser(description=(
'Display all known regions and their parameters. '
'To include any regions in the board overlay, run '
'"make overlay-$BOARD && overlay-$BOARD/py/l10n/regions.py" '
'from the platform/factory directory.'))
parser.add_argument('--format', choices=('human-readable', 'csv'),
default='human-readable',
help='Output format (default=%(default)s)')
parser.add_argument('--all', action='store_true',
help='Include unconfirmed and incomplete regions')
args = parser.parse_args()
# List of list of lines to print.
lines = [Region.FIELDS]
for region in sorted(BuildRegionsDict(args.all).values(),
key=lambda v: v.region_code):
lines.append([getattr(region, field) for field in Region.FIELDS])
if args.format == 'csv':
# Just print the lines in CSV format.
for l in lines:
print ','.join(l)
elif args.format == 'human-readable':
num_columns = len(lines[0])
# Calculate maximum length of each column.
max_lengths = []
for column_no in xrange(num_columns):
max_lengths.append(max(len(line[column_no]) for line in lines))
# Print each line, padding as necessary to the max column length.
for line in lines:
for column_no in xrange(num_columns):
sys.stdout.write(line[column_no].ljust(max_lengths[column_no] + 2))
sys.stdout.write('\n')
if __name__ == '__main__':
main()