jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/368800 )
Change subject: Image adaptation with namespace alias change
......................................................................
Image adaptation with namespace alias change
* Adapts an image if the image is from commons.
* Introduces NamespaceAlias request and caching mechanism.
* Image alignment classes are flipped based on language directions.
* To check language directionality, a node module language-data is used.
* Tests included.
language-data node module is a fresh node module written based
on jquery.uls by porting the data and utilities to nodejs.
It is not yet published in npm. Temporarily hosted at
github.com/santhoshtr/language-data, and wish to host under
wikimedia repo.
Bug: T170674
Change-Id: I288f67fbe94371ee8039ecdaf69984e51aa43511
---
M lib/mw/APIRequestManager.js
M lib/mw/ApiRequest.js
A lib/translationunits/MWImage.js
M lib/translationunits/index.js
M package.json
M test/adaptation/AdaptationTests.json
6 files changed, 149 insertions(+), 7 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/lib/mw/APIRequestManager.js b/lib/mw/APIRequestManager.js
index 1e8f673..e8d0739 100644
--- a/lib/mw/APIRequestManager.js
+++ b/lib/mw/APIRequestManager.js
@@ -1,4 +1,5 @@
-const TitlePairRequest = require( './TitlePairRequest.js' );
+const TitlePairRequest = require( './TitlePairRequest.js' ),
+ APIRequest = require( './ApiRequest.js' );
class MWAPIRequestManager {
constructor( appContext ) {
@@ -27,6 +28,31 @@
return instance.get( title );
}
+
+ /**
+ * For a given canonical namespace, get the namespace alias in given
target language.
+ * @param {string} canonicalNamespace Canonical namespace. Example:
File, Talk, Special etc.
+ * See https://www.mediawiki.org/wiki/Help:Namespaces
+ * @param {string} targetLanguage Target language code
+ * @return {Promise}
+ */
+ getNamespaceAlias( canonicalNamespace, targetLanguage ) {
+ var instance = new APIRequest( { targetLanguage, context:
this.context } );
+
+ // SiteInfo is cached in APIRequest, so we are not adding any
caching here.
+ return instance.getSiteInfo( targetLanguage ).then( ( siteInfo
) => {
+ var namespaceId;
+ for ( namespaceId in siteInfo.namespaces ) {
+ if ( siteInfo.namespaces.hasOwnProperty(
namespaceId ) ) {
+ if ( siteInfo.namespaces[ namespaceId
].canonical === canonicalNamespace ) {
+ return siteInfo.namespaces[
namespaceId ].name;
+ }
+ }
+ }
+ // Fallback
+ return canonicalNamespace;
+ } );
+ }
}
/**
diff --git a/lib/mw/ApiRequest.js b/lib/mw/ApiRequest.js
index 845d5d0..4bda2b5 100644
--- a/lib/mw/ApiRequest.js
+++ b/lib/mw/ApiRequest.js
@@ -10,8 +10,8 @@
/**
* @param {Object} config Configuration options
* @cfg {Object} context Application context
- * @cfg {string} sourceLanguage Source language
- * @cfg {string} targetLanguage target language
+ * @cfg {string} [sourceLanguage] Source language
+ * @cfg {string} [targetLanguage] target language
*/
constructor( config ) {
this.context = config.context;
diff --git a/lib/translationunits/MWImage.js b/lib/translationunits/MWImage.js
new file mode 100644
index 0000000..e3b24ca
--- /dev/null
+++ b/lib/translationunits/MWImage.js
@@ -0,0 +1,93 @@
+const cxutil = require( '../util.js' ),
+ TranslationUnit = require( './TranslationUnit.js' ),
+ languageData = require( 'language-data' ),
+ MWAPIRequestManager = require( '../mw/APIRequestManager.js' ),
+ CommonsFilePatchPrefix = '//upload.wikimedia.org/wikipedia/commons/';
+
+class MWImage extends TranslationUnit {
+ constructor( node, sourceLanguage, targetLanguage, context ) {
+ super( node, sourceLanguage, targetLanguage, context );
+ }
+
+ /**
+ * Check if an image is coming from Commons or not. Uses the URL
pattern of the common file
+ * repository to determine whether the image is stored there.
+ * @param {string} imageSrc
+ * @return {boolean}
+ */
+ isCommonsImage( imageSrc ) {
+ return imageSrc.indexOf( CommonsFilePatchPrefix ) === 0;
+ }
+
+ /**
+ * Adapt the image's alignment settings for the target language.
+ */
+ adaptImageAlignment() {
+ var classes, targetDirection, sourceDirection, leftIndex,
rightIndex;
+
+ sourceDirection = languageData.getDir( this.sourceLanguage );
+ targetDirection = languageData.getDir( this.targetLanguage );
+
+ if ( sourceDirection === targetDirection ) {
+ // Source and target languages has same directionality.
Nothing to do
+ return;
+ }
+
+ classes = this.node.attributes.class.split( ' ' );
+ // If the image has an explicit alignment class in HTML, this
means that it has explicit
+ // alignment defined in wiki syntax. It must be explicitly
flipped if the target language's
+ // direction is different.
+ leftIndex = classes.indexOf( 'mw-halign-left' );
+ rightIndex = classes.indexOf( 'mw-halign-right' );
+ if ( leftIndex > -1 ) {
+ classes[ leftIndex ] = 'mw-halign-right';
+ } else if ( rightIndex > -1 ) {
+ classes[ rightIndex ] = 'mw-halign-left';
+ }
+
+ this.node.attributes.class = classes.join( ' ' );
+ }
+}
+
+MWImage.prototype.adapt = cxutil.async( function* () {
+ var i, len, chunk, sourceImage, imageLink, targetResource,
namespaceAlias;
+
+ for ( i = 0, len = this.node.children.textChunks.length; i < len; i++ )
{
+ chunk = this.node.children.textChunks[ i ];
+ if ( chunk.tags[ 0 ].name === 'a' ) {
+ imageLink = chunk.tags[ 0 ];
+ }
+ if ( chunk.inlineContent && chunk.inlineContent.name === 'img'
) {
+ sourceImage = chunk.inlineContent;
+ break;
+ }
+ }
+
+ if ( !sourceImage ) {
+ throw new Error( 'img tag not found in the figure with
mw:Image/Thumb for id: ' + this.node.attributes.id );
+ }
+
+ this.sourceResource = sourceImage.attributes[ 'resource' ];
+ this.adaptImageAlignment();
+
+ if ( this.isCommonsImage( sourceImage.attributes[ 'src' ] ) ) {
+ namespaceAlias = yield new MWAPIRequestManager( this.context
).getNamespaceAlias( 'File', this.targetLanguage );
+ targetResource = this.sourceResource.replace(
/^(\.+\/)*(.+)(:)/, '$1' + namespaceAlias + '$3' );
+ sourceImage.attributes[ 'resource' ] = imageLink.attributes[
'href' ] = targetResource;
+ } else {
+ // TODO: This format is not decided yet. We do need to inform
client about failed
+ // adaptations somehow.
+ this.node.attributes[ 'data-cx' ] = JSON.stringify( {
+ adapted: false,
+ imageSource: this.imageSource,
+ resource: this.sourceResource
+ } );
+ }
+ return this.node;
+} );
+
+MWImage.name = 'image';
+MWImage.matchTagNames = [ 'figure' ];
+MWImage.matchRdfaTypes = [ 'mw:Image', 'mw:Image/Thumb', 'mw:Image/Frame',
'mw:Image/Frameless' ];
+
+module.exports = MWImage;
diff --git a/lib/translationunits/index.js b/lib/translationunits/index.js
index 38df6e2..402406f 100644
--- a/lib/translationunits/index.js
+++ b/lib/translationunits/index.js
@@ -1,3 +1,4 @@
module.exports = {
- MWLink: require( './MWLink.js' )
+ MWLink: require( './MWLink.js' ),
+ MWImage: require( './MWImage.js' )
};
diff --git a/package.json b/package.json
index 8bc1bc6..9e891d5 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,6 @@
"node": ">=4.4.6"
},
"dependencies": {
- "jsonwebtoken": "~5.6.0",
- "sax": "1.2.1",
- "html-entities": "1.2.0",
"bluebird": "^3.4.1",
"body-parser": "^1.15.2",
"bunyan": "^1.8.1",
@@ -26,9 +23,13 @@
"core-js": "^2.4.1",
"domino": "^1.0.25",
"express": "^4.14.0",
+ "html-entities": "1.2.0",
"js-yaml": "^3.6.1",
+ "jsonwebtoken": "~5.6.0",
+ "language-data": "git+https://github.com/santhoshtr/language-data#master",
"mediawiki-title": "^0.6.3",
"preq": "^0.5.2",
+ "sax": "1.2.1",
"service-runner": "^2.2.5",
"swagger-router": "^0.4.6",
"swagger-ui": "git+https://github.com/wikimedia/swagger-ui#master",
diff --git a/test/adaptation/AdaptationTests.json
b/test/adaptation/AdaptationTests.json
index a96d619..a97d3d6 100644
--- a/test/adaptation/AdaptationTests.json
+++ b/test/adaptation/AdaptationTests.json
@@ -12,5 +12,26 @@
"to": "es",
"source": "<p id='mwBg'><b id='mwBw'>Oxygen</b> is a <a
rel='mw:WikiLink' href='./Chemical_element' title='Chemical element'
id='mwCA'>chemical element</a> with symbol<span typeof='mw:Entity' id='mwCQ'>
</span><b id='mwCg'>O</b> and <a rel='mw:WikiLink' href='./Atomic_number'
title='Atomic number' id='mwCw'>atomic number</a> 8. It is a member of the <a
rel='mw:WikiLink' href='./Chalcogen' title='Chalcogen' id='mwDA'>chalcogen</a>
<a rel='mw:WikiLink' href='./Group_(periodic_table)' title='Group (periodic
table)' id='mwDQ'>group</a> on the <a rel='mw:WikiLink' href='./Periodic_table'
title='Periodic table' id='mwDg'>periodic table</a> and is a highly <a
rel='mw:WikiLink' href='./Chemical_reaction' title='Chemical reaction'
id='mwDw'>reactive</a> <a rel='mw:WikiLink' href='./Nonmetal' title='Nonmetal'
id='mwEA'>nonmetal</a> and <a rel='mw:WikiLink' href='./Oxidizing_agent'
title='Oxidizing agent' id='mwEQ'>oxidizing agent</a> that readily forms <a
rel='mw:WikiLink' href='./Oxide' title='Oxide' id='mwEg'>oxides</a> with most
elements as well as other <a rel='mw:WikiLink' href='./Chemical_compound'
title='Chemical compound' id='mwEw'>compounds</a>. By mass, oxygen is the
third-<a rel='mw:WikiLink' href='./Abundance_of_the_chemical_elements'
title='Abundance of the chemical elements' id='mwFA'>most abundant element</a>
in the universe, after <a rel='mw:WikiLink' href='./Hydrogen' title='Hydrogen'
id='mwFQ'>hydrogen</a> and <a rel='mw:WikiLink' href='./Helium' title='Helium'
id='mwFg'>helium</a>. At <a rel='mw:WikiLink'
href='./Standard_temperature_and_pressure' title='Standard temperature and
pressure' id='mwFw' class='mw-redirect'>standard temperature and pressure</a>,
two atoms of the element <a rel='mw:WikiLink' href='./Chemical_bond'
title='Chemical bond' id='mwGA'>bind</a> to form <a rel='mw:WikiLink'
href='./Allotropes_of_oxygen#Dioxygen' title='Allotropes of oxygen'
id='mwGQ'>dioxygen</a>, a colorless and odorless <a rel='mw:WikiLink'
href='./Diatomic_molecule' title='Diatomic molecule' id='mwGg'>diatomic</a> <a
rel='mw:WikiLink' href='./Gas' title='Gas' id='mwGw'>gas</a> with the formula.
This is an important part of the <a rel='mw:WikiLink'
href='./Atmosphere_of_Earth' title='Atmosphere of Earth'
id='mwHQ'>atmosphere</a> and diatomic oxygen gas constitutes 20.8% of the <a
rel='mw:WikiLink' href='./Earth's_atmosphere' title='Earth's atmosphere'
id='mwHg' class='mw-redirect'>Earth's atmosphere</a>. Additionally, as oxides
the element makes up almost half of the <a rel='mw:WikiLink'
href='./Earth's_crust' title='Earth's crust' id='mwHw'
class='mw-redirect'>Earth's crust</a>.</p>",
"result": "<p id='mwBg'><b id='mwBw'>Oxygen</b> is a <a
href='Elemento químico' id='mwCA' rel='mw:WikiLink' title='Chemical
element'>chemical element</a> with symbol<span id='mwCQ' typeof='mw:Entity'>
</span><b id='mwCg'>O</b> and <a href='Número atómico' id='mwCw'
rel='mw:WikiLink' title='Atomic number'>atomic number</a> 8. It is a member of
the <a href='Anfígeno' id='mwDA' rel='mw:WikiLink'
title='Chalcogen'>chalcogen</a> <a
data-cx='{"adapted":false,"sourceTitle":"Group (periodic
table)"}' href='./Group_(periodic_table)' id='mwDQ' rel='mw:WikiLink'
title='Group (periodic table)'>group</a> on the <a href='Tabla periódica de los
elementos' id='mwDg' rel='mw:WikiLink' title='Periodic table'>periodic
table</a> and is a highly <a href='Reacción química' id='mwDw'
rel='mw:WikiLink' title='Chemical reaction'>reactive</a> <a href='No metal'
id='mwEA' rel='mw:WikiLink' title='Nonmetal'>nonmetal</a> and <a
href='Oxidante' id='mwEQ' rel='mw:WikiLink' title='Oxidizing agent'>oxidizing
agent</a> that readily forms <a href='Óxido' id='mwEg' rel='mw:WikiLink'
title='Oxide'>oxides</a> with most elements as well as other <a href='Compuesto
químico' id='mwEw' rel='mw:WikiLink' title='Chemical compound'>compounds</a>.
By mass, oxygen is the third-<a href='Abundancia de los elementos químicos'
id='mwFA' rel='mw:WikiLink' title='Abundance of the chemical elements'>most
abundant element</a> in the universe, after <a href='Hidrógeno' id='mwFQ'
rel='mw:WikiLink' title='Hydrogen'>hydrogen</a> and <a href='Helio' id='mwFg'
rel='mw:WikiLink' title='Helium'>helium</a>. At <a class='mw-redirect'
href='Condiciones normalizadas de presión y temperatura' id='mwFw'
rel='mw:WikiLink' title='Standard temperature and pressure'>standard
temperature and pressure</a>, two atoms of the element <a href='Enlace químico'
id='mwGA' rel='mw:WikiLink' title='Chemical bond'>bind</a> to form <a
href='Alótropos del oxígeno' id='mwGQ' rel='mw:WikiLink' title='Allotropes of
oxygen'>dioxygen</a>, a colorless and odorless <a href='Molécula diatómica'
id='mwGg' rel='mw:WikiLink' title='Diatomic molecule'>diatomic</a> <a
href='Gas' id='mwGw' rel='mw:WikiLink' title='Gas'>gas</a> with the formula.
This is an important part of the <a href='Atmósfera terrestre' id='mwHQ'
rel='mw:WikiLink' title='Atmosphere of Earth'>atmosphere</a> and diatomic
oxygen gas constitutes 20.8% of the <a atmosphere='' class='mw-redirect'
href='Tierra' id='mwHg' rel='mw:WikiLink' s='' s_atmosphere=''
title='Earth'>Earth's atmosphere</a>. Additionally, as oxides the element makes
up almost half of the <a class='mw-redirect' crust='' href='Tierra' id='mwHw'
rel='mw:WikiLink' s='' s_crust='' title='Earth'>Earth's crust</a>.</p>"
+ },
+ {
+ "desc": "Image adaptation basic test",
+ "from": "en",
+ "to": "ru",
+ "source": "<figure class='mw-default-size mw-halign-left'
typeof='mw:Image/Thumb' id='mwUQ'><a
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
resource='./File:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
data-file-width='125' data-file-height='248' data-file-type='bitmap'
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium'
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink'
href='./Detective' title='Detective'
id='mwVg'>investigators</a>.</figcaption></figure>",
+ "result": "<figure class='mw-default-size mw-halign-left'
id='mwUQ' typeof='mw:Image/Thumb'><a
href='./Файл:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
data-file-height='248' data-file-type='bitmap' data-file-width='125'
height='248' id='mwUw'
resource='./Файл:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
width='125' /></a><figcaption id='mwVA'><a href='Филон Византийский' id='mwVQ'
rel='mw:WikiLink' title='Philo of Byzantium'>Philo's</a> experiment inspired
later <a href='Детектив (профессия)' id='mwVg' rel='mw:WikiLink'
title='Detective'>investigators</a>.</figcaption></figure>"
+ },
+ {
+ "desc": "Image adaptation- source and target language
directions differs",
+ "from": "en",
+ "to": "he",
+ "source": "<figure class='mw-default-size mw-halign-left'
typeof='mw:Image/Thumb' id='mwUQ'><a
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
resource='./File:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
data-file-width='125' data-file-height='248' data-file-type='bitmap'
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium'
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink'
href='./Detective' title='Detective'
id='mwVg'>investigators</a>.</figcaption></figure>",
+ "result": "<figure class='mw-default-size mw-halign-right'
id='mwUQ' typeof='mw:Image/Thumb'><a
href='./קובץ:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
data-file-height='248' data-file-type='bitmap' data-file-width='125'
height='248' id='mwUw'
resource='./קובץ:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
width='125' /></a><figcaption id='mwVA'><a
data-cx='{"adapted":false,"sourceTitle":"Philo of
Byzantium"}' href='./Philo_of_Byzantium' id='mwVQ' rel='mw:WikiLink'
title='Philo of Byzantium'>Philo's</a> experiment inspired later <a
data-cx='{"adapted":false,"sourceTitle":"Detective"}'
href='./Detective' id='mwVg' rel='mw:WikiLink'
title='Detective'>investigators</a>.</figcaption></figure>"
+ },
+ {
+ "desc": "Image adaptation- image is not in commons",
+ "from": "en",
+ "to": "ml",
+ "source": "<figure class='mw-default-size mw-halign-left'
typeof='mw:Image/Frame' id='mwUQ'><a
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
resource='./File:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/mediawiki/e/e6/Philos_experiment_of_the_burning_candle.PNG'
data-file-width='125' data-file-height='248' data-file-type='bitmap'
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium'
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink'
href='./Detective' title='Detective'
id='mwVg'>investigators</a>.</figcaption></figure>",
+ "result": "<figure class='mw-default-size mw-halign-left'
data-cx='{"adapted":false,"resource":"./File:Philos_experiment_of_the_burning_candle.PNG"}'
id='mwUQ' typeof='mw:Image/Frame'><a
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img
alt='Drawing of a burning candle enclosed in a glass bulb.'
data-file-height='248' data-file-type='bitmap' data-file-width='125'
height='248' id='mwUw'
resource='./File:Philos_experiment_of_the_burning_candle.PNG'
src='//upload.wikimedia.org/wikipedia/mediawiki/e/e6/Philos_experiment_of_the_burning_candle.PNG'
width='125' /></a><figcaption id='mwVA'><a
data-cx='{"adapted":false,"sourceTitle":"Philo of
Byzantium"}' href='./Philo_of_Byzantium' id='mwVQ' rel='mw:WikiLink'
title='Philo of Byzantium'>Philo's</a> experiment inspired later <a
data-cx='{"adapted":false,"sourceTitle":"Detective"}'
href='./Detective' id='mwVg' rel='mw:WikiLink'
title='Detective'>investigators</a>.</figcaption></figure>"
}
]
--
To view, visit https://gerrit.wikimedia.org/r/368800
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I288f67fbe94371ee8039ecdaf69984e51aa43511
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/services/cxserver
Gerrit-Branch: master
Gerrit-Owner: Santhosh <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Santhosh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits