Skip to content

Commit

Permalink
feat(japanese): Add Japanese language variant for labels in Japan
Browse files Browse the repository at this point in the history
  • Loading branch information
orangejulius committed Mar 22, 2022
1 parent b943ac3 commit dbb7d25
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 0 deletions.
83 changes: 83 additions & 0 deletions builders/JPN-JPN.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const _ = require('lodash');

// get the value of a scalar, or the first element of an array if an array is passed
function scalarOrFirstElement(param) {
return _.isArray(param) ? param[0] : param;
}

function formatPostalCode(record) {
if (record.postalcode) {
return '〒' + record.postalcode;
}
}

function buildAdminLabelPart(schema, record) {
let labelParts = [];

// iterate the admin components in the schema
// the order of the properties in the file determine
// the order of elements in this array
for (const field in schema.valueFunctions) {
const valueFunction = schema.valueFunctions[field];

labelParts.push(valueFunction(record));
}

return labelParts;
}

// generally, our data from OA contains the 'chome' or district (similar to a neighbourhood)
// in the street field, even though it really is not a street at all
// detect this case and then return the street value
function formatDistrictPart(record) {
if (scalarOrFirstElement(record.name.default).includes(record.street)) {
return record.street;
}
}

// check for records with a standard Japanese building and block number in the housenumber field
// these consist of two sets of numbers separated by a -
// for Japanese format language, reformat them with the "番号" or "bango" characters
function formatBlockPart(record) {
const bangoRegex = new RegExp(/(\d+)-(\d+)/);

const match = bangoRegex.exec(record.housenumber);
if (match) {
return `${match[1]}${match[2]}号`;
}

return record.housenumber;
}

function venueName(record) {
if (record.layer !== 'venue') {
return;
}

return scalarOrFirstElement(record.name.ja) || scalarOrFirstElement(record.name.default);
}

// builds a complete label by combining several components
// first, the postalcode if it exists, prefixed by the postal mark
// second, administrative information like city
// third, street/block level info if it exists, formatted as correctly as possible
// finally, the venue name if it exists
function japanBuilder(schema, record) {

const postalcode = formatPostalCode(record);
const admin = buildAdminLabelPart(schema, record);
const distric = formatDistrictPart(record);
const block = formatBlockPart(record);
const venue = venueName(record);

// combine components, inserting a space between the postalcode, admin, and name sections
// when written on an envelope these would correspond to new lines, a space should help with some clarity
let labelParts = _.concat(postalcode, ' ', admin, distric, block, ' ', venue);

// retain only things that are truthy
labelParts = _.compact(labelParts);

return labelParts;
}

module.exports = japanBuilder;
22 changes: 22 additions & 0 deletions labelSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,28 @@ module.exports = {
}
},
'JPN': {
'languages': {
'JPN': {
'valueFunctions': {
'prefecture': getFirstProperty(['region']),

// outside of designated cities, this is the city name
'city': getFirstProperty(['county']),

// 20 important cities in Japan are known as 'designated cities'
// https://en.wikipedia.org/wiki/Cities_designated_by_government_ordinance_of_Japan
'designated-city': getFirstProperty(['locality']),

'machi': getFirstProperty(['borough']), // this is a middle layer between city and district for larger cities

'district': getFirstProperty(['neighbourhood']),
},
'meta': {
'builder': require('./builders/JPN-JPN'),
'separator': '' // no separation between most elements in Japanese
}
}
},
'valueFunctions': {
'district': getFirstProperty(['neighbourhood']),
'machi': getFirstProperty(['borough']), // this is a middle layer between city and district for larger cities
Expand Down
8 changes: 8 additions & 0 deletions test/getSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ module.exports.tests.schema_and_language_selection = function(test, common) {
t.equals(actual, expected, 'USA schema selected');
t.end();
});

test('valid language and valid country_a combination selects that language variant for country', function(t) {
const actual = getSchema({ country_a: ['JPN']}, 'JPN'); // There is a Japanese specific label format for records in Japan
const expected = schemas.JPN.languages.JPN;

t.equals(actual, expected, 'Japanese schema for Japanese language selected');
t.end();
});
};

module.exports.all = function (tape, common) {
Expand Down
164 changes: 164 additions & 0 deletions test/labelGenerator_JPN_JPN.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
const generator = require('../labelGenerator');

module.exports.tests = {};

module.exports.tests.english_style_labels = function(test, common) {
test('tokyo post office', function(t) {
const doc = {
name: { default: '東京中央郵便局' },
layer: 'venue',
housenumber: '2', // this is incorrect but it's what we currently pull from OSM
locality: ['東京'],
county: ['千代田'],
region: ['東京'],
region_a: ['TK'], // should not be used
postalcode: ['100-0005'],
country_a: ['JPN'], // should not be used
country: ['日本'], // should not be used (though this is up for debate)
continent: ['アジア'], // should not be used
};

const expected = '〒100-0005 ' + // postalcode with postal mark, plus a space (would be a newline on an envelope)
'東京' + // region, tokyo-to
'千代田' + // county, chyoda ward
'東京' + // locality, tokyo
'2 ' + // housenumber plus space to separate venue name
'東京中央郵便局'; // venue name: 'Tokyo Central Post Office'
t.equal(generator(doc, 'JPN'), expected);
t.end();
});

test('tokyo address from OA', function(t) {
const doc = {
name: { default: '7-9 丸の内二丁目' },
layer: 'address',
housenumber: '7-9',
street: '丸の内二丁目', // Marunouchi 2-chome
borough: ['千代田区'], // Chiyoda Ward
locality: ['東京'], // Tokyo
county: ['千代田'], // Chiyoda
region: ['東京'], // Tokyo (should really be 東京都 for Tokyo-to)
region_a: ['TK'],
country: ['日本'], // Japan
country_a: ['JPN'],
continent: ['アジア'], // Asia
};

const expected = '' + // no postalcode for this record
'東京' + // prefecture (region), tokyo
'千代田' + // county, chyoda
'東京' + // designated city (locality), tokyo
'千代田区' + // ward (borough), chiyoda ward
'丸の内二丁目' + // district (street), Marunouchi 2-chome
'7番9号'; // "bango" (block and building number)
t.equal(generator(doc, 'JPN'), expected);
t.end();
});

test('Shizuoka (smaller city near Tokyo) example from OA', function(t) {
const doc = {
name: { default: '331-8 中原' },
layer: 'address',
housenumber: '331-8',
street: '中原',
// we should ideally have lower level admin info here, but we don't as of this writing
locality: ['静岡市'],
county: ['静岡'],
region: ['東京'], // Tokyo (should really be 東京都 for Tokyo-to)
region_a: ['SZ'],
country: ['日本'],
country_a: ['JPN'],
continent: ['アジア'],
};

const expected = '' + // no postalcode for this record
'東京' + // prefecture (region), tokyo
'静岡' + // city (county), Shizuoka
'静岡市' + // designated city (locality), Shizuoka-shi
'中原' + // chome/ward (street), Nakahara
'331番8号'; // "bango" (block and building number)
t.equal(generator(doc, 'JPN'), expected);
t.end();
});

test('address in Sapporo from OA', function(t) {
const doc = {
name: { default: '2-12 北二十四条西四丁目' },
layer: 'address',
housenumber: '2-12',
street: '北二十四条西四丁目',
locality: ['札幌市'],
county: ['札幌'],
region: ['北海'],
region_a: ['HK'],
country: ['日本'],
country_a: ['JPN'],
continent: ['アジア'],
};
const expected = '' + // no postalcode for this record
'北海' + // prefecture (region), Hokkaido Prefecture
'札幌' + // city (county), Sapporo
'札幌市' + // designated city (locality), Sapporo-shi
'北二十四条西四丁目' + // chome/ward (street)
'2番12号'; // "bango" (block and building number)

t.equal(generator(doc, 'JPN'), expected);
t.end();
});

test('address outside of a designated city', function(t) {
const doc = {
name: { default: '1059-2 大崎' },
layer: 'address',
housenumber: '1059-2',
street: '大崎',
county: ['小郡市'],
region: ['福岡'],
region_a: ['FO'],
country: ['日本'],
country_a: ['JPN'],
continent: ['アジア'],
};

const expected = '' + // no postalcode for this record
'福岡' + // prefecture (region), Fukuoka Prefecture
'小郡市' + // city (county), Ogori
'大崎' + // chome/ward (street), Osaki
'1059番2号'; // "bango" (block and building number)
t.equal(generator(doc, 'JPN'), expected);
t.end();
});

test('neighbourhood admin area', function(t) {
const doc = {
name: { default: '9丁目' },
layer: 'neighbourhood',
neighbourhood: ['9丁目'],
locality: ['世田谷区'],
county: ['世田谷区'],
region: ['東京'],
region_a: ['TK'],
country: ['日本'],
country_a: ['JPN'],
continent: ['アジア'],
};

const expected = '東京' + // prefecture (region), Tokyo
'世田谷区' + // city (county), Setagaya
'世田谷区' + // designated city (locality), Setagaya
'9丁目'; // ward (neighbourhoood)

t.equal(generator(doc, 'JPN'), expected);
t.end();
});
};

module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (JPN, 日本ese language): ' + name, testFunction);
}

for( let testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};
1 change: 1 addition & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var tests = [
require ('./labelGenerator_USA'),
require ('./labelGenerator_KOR'),
require ('./labelGenerator_JPN'),
require ('./labelGenerator_JPN_JPN'),
require ('./labelGenerator_FRA'),
require ('./getSchema'),
require ('./labelSchema')
Expand Down

0 comments on commit dbb7d25

Please sign in to comment.