mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-23 14:06:52 +00:00
screenshots: Add multi-language support
Change-Id: I1da41cffd946a76e5f09097decbc0a25562c9508
This commit is contained in:
parent
746321a29f
commit
c3fa707adc
26
Gruntfile.js
26
Gruntfile.js
|
@ -56,10 +56,29 @@ module.exports = function ( grunt ) {
|
|||
}
|
||||
},
|
||||
mochaTest: {
|
||||
screenshots: {
|
||||
'screenshots-en': {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: 20000
|
||||
timeout: 20000,
|
||||
require: [
|
||||
function () {
|
||||
/* jshint undef:false */
|
||||
langs = [ 'en' ];
|
||||
}
|
||||
]
|
||||
},
|
||||
src: [ 'build/screenshots.js' ]
|
||||
},
|
||||
'screenshots-all': {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: 20000,
|
||||
require: [
|
||||
function () {
|
||||
/* jshint undef:false */
|
||||
langs = require( './build/tasks/screenshotLangs.json' ).langs;
|
||||
}
|
||||
]
|
||||
},
|
||||
src: [ 'build/screenshots.js' ]
|
||||
}
|
||||
|
@ -163,7 +182,8 @@ module.exports = function ( grunt ) {
|
|||
grunt.registerTask( 'fix', [ 'jscs:fix' ] );
|
||||
grunt.registerTask( 'test', [ 'build', 'lint' ] );
|
||||
grunt.registerTask( 'test-ci', [ 'git-status' ] );
|
||||
grunt.registerTask( 'screenshots', [ 'mochaTest:screenshots' ] );
|
||||
grunt.registerTask( 'screenshots', [ 'mochaTest:screenshots-en' ] );
|
||||
grunt.registerTask( 'screenshots-all', [ 'mochaTest:screenshots-all' ] );
|
||||
grunt.registerTask( 'default', 'test' );
|
||||
|
||||
if ( process.env.JENKINS_HOME ) {
|
||||
|
|
|
@ -1,266 +1,271 @@
|
|||
/* jshint node: true */
|
||||
/* global seleniumUtils */
|
||||
|
||||
var chrome = require( 'selenium-webdriver/chrome' ),
|
||||
/* global seleniumUtils, langs */
|
||||
var i, l,
|
||||
chrome = require( 'selenium-webdriver/chrome' ),
|
||||
test = require( 'selenium-webdriver/testing' ),
|
||||
fs = require( 'fs' ),
|
||||
Jimp = require( 'jimp' ),
|
||||
lang = 'en';
|
||||
Jimp = require( 'jimp' );
|
||||
|
||||
test.describe( 'Screenshot', function () {
|
||||
var driver;
|
||||
function runTests( lang ) {
|
||||
test.describe( 'Screenshots: ' + lang, function () {
|
||||
var driver;
|
||||
|
||||
function runScreenshotTest( name, clientScript, padding ) {
|
||||
var filename = './screenshots/' + name + '-' + lang + '.png';
|
||||
driver.get( 'http://en.wikipedia.beta.wmflabs.org/wiki/PageDoesNotExist?veaction=edit&uselang=' + lang );
|
||||
driver.wait(
|
||||
driver.executeAsyncScript(
|
||||
test.beforeEach( function () {
|
||||
driver = new chrome.Driver();
|
||||
driver.manage().timeouts().setScriptTimeout( 20000 );
|
||||
driver.manage().window().setSize( 1200, 800 );
|
||||
} );
|
||||
|
||||
test.afterEach( function () {
|
||||
driver.quit();
|
||||
} );
|
||||
|
||||
function runScreenshotTest( name, clientScript, padding ) {
|
||||
var filename = './screenshots/' + name + '-' + lang + '.png';
|
||||
driver.get( 'http://en.wikipedia.beta.wmflabs.org/wiki/PageDoesNotExist?veaction=edit&uselang=' + lang );
|
||||
driver.wait(
|
||||
driver.executeAsyncScript(
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
|
||||
window.seleniumUtils = {
|
||||
getBoundingRect: function ( elements ) {
|
||||
var i, l, rect, boundingRect;
|
||||
for ( i = 0, l = elements.length; i < l; i++ ) {
|
||||
rect = elements[ i ].getBoundingClientRect();
|
||||
if ( !boundingRect ) {
|
||||
boundingRect = {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom
|
||||
};
|
||||
} else {
|
||||
boundingRect.left = Math.min( boundingRect.left, rect.left );
|
||||
boundingRect.top = Math.min( boundingRect.top, rect.top );
|
||||
boundingRect.right = Math.max( boundingRect.right, rect.right );
|
||||
boundingRect.bottom = Math.max( boundingRect.bottom, rect.bottom );
|
||||
}
|
||||
}
|
||||
if ( boundingRect ) {
|
||||
boundingRect.width = boundingRect.right - boundingRect.left;
|
||||
boundingRect.height = boundingRect.bottom - boundingRect.top;
|
||||
}
|
||||
return boundingRect;
|
||||
}
|
||||
};
|
||||
|
||||
// Suppress welcome dialog
|
||||
localStorage.setItem( 've-beta-welcome-dialog', 1 );
|
||||
// Suppress user education indicators
|
||||
localStorage.setItem( 've-hideusered', 1 );
|
||||
mw.hook( 've.activationComplete' ).add( function () {
|
||||
var target = ve.init.target,
|
||||
surfaceView = target.getSurface().getView();
|
||||
// Hide edit notices
|
||||
target.actionsToolbar.tools.notices.getPopup().toggle( false );
|
||||
// Modify the document to make the save button blue
|
||||
// Wait for focus
|
||||
surfaceView.once( 'focus', function () {
|
||||
target.surface.getModel().getFragment().insertContent( ' ' ).collapseToStart().select();
|
||||
// Wait for save button fade
|
||||
setTimeout( done, 100 );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
).then( function () {
|
||||
return driver.executeAsyncScript( clientScript ).then( function ( rect ) {
|
||||
return driver.takeScreenshot().then( function ( base64Image ) {
|
||||
var imageBuffer;
|
||||
if ( rect ) {
|
||||
imageBuffer = new Buffer( base64Image, 'base64' );
|
||||
return cropScreenshot( filename, imageBuffer, rect, padding );
|
||||
} else {
|
||||
fs.writeFile( filename, base64Image, 'base64' );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} ),
|
||||
20000
|
||||
);
|
||||
}
|
||||
|
||||
function cropScreenshot( filename, imageBuffer, rect, padding ) {
|
||||
if ( padding === undefined ) {
|
||||
padding = 5;
|
||||
}
|
||||
|
||||
return Jimp.read( imageBuffer ).then( function ( jimpImage ) {
|
||||
jimpImage
|
||||
.crop(
|
||||
rect.left - padding,
|
||||
rect.top - padding,
|
||||
rect.width + ( padding * 2 ),
|
||||
rect.height + ( padding * 2 )
|
||||
)
|
||||
.write( filename );
|
||||
} );
|
||||
}
|
||||
|
||||
test.it( 'Toolbar', function () {
|
||||
runScreenshotTest( 'VisualEditor_toolbar',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
|
||||
window.seleniumUtils = {
|
||||
getBoundingRect: function ( elements ) {
|
||||
var i, l, rect, boundingRect;
|
||||
for ( i = 0, l = elements.length; i < l; i++ ) {
|
||||
rect = elements[ i ].getBoundingClientRect();
|
||||
if ( !boundingRect ) {
|
||||
boundingRect = {
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom
|
||||
};
|
||||
} else {
|
||||
boundingRect.left = Math.min( boundingRect.left, rect.left );
|
||||
boundingRect.top = Math.min( boundingRect.top, rect.top );
|
||||
boundingRect.right = Math.max( boundingRect.right, rect.right );
|
||||
boundingRect.bottom = Math.max( boundingRect.bottom, rect.bottom );
|
||||
}
|
||||
}
|
||||
if ( boundingRect ) {
|
||||
boundingRect.width = boundingRect.right - boundingRect.left;
|
||||
boundingRect.height = boundingRect.bottom - boundingRect.top;
|
||||
}
|
||||
return boundingRect;
|
||||
}
|
||||
};
|
||||
|
||||
// Suppress welcome dialog
|
||||
localStorage.setItem( 've-beta-welcome-dialog', 1 );
|
||||
// Suppress user education indicators
|
||||
localStorage.setItem( 've-hideusered', 1 );
|
||||
mw.hook( 've.activationComplete' ).add( function () {
|
||||
var target = ve.init.target,
|
||||
surfaceView = target.getSurface().getView();
|
||||
// Hide edit notices
|
||||
target.actionsToolbar.tools.notices.getPopup().toggle( false );
|
||||
// Modify the document to make the save button blue
|
||||
// Wait for focus
|
||||
surfaceView.once( 'focus', function () {
|
||||
target.surface.getModel().getFragment().insertContent( ' ' ).collapseToStart().select();
|
||||
// Wait for save button fade
|
||||
setTimeout( done, 100 );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
).then( function () {
|
||||
return driver.executeAsyncScript( clientScript ).then( function ( rect ) {
|
||||
return driver.takeScreenshot().then( function ( base64Image ) {
|
||||
var imageBuffer;
|
||||
if ( rect ) {
|
||||
imageBuffer = new Buffer( base64Image, 'base64' );
|
||||
return cropScreenshot( filename, imageBuffer, rect, padding );
|
||||
} else {
|
||||
fs.writeFile( filename, base64Image, 'base64' );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
} ),
|
||||
20000
|
||||
);
|
||||
}
|
||||
|
||||
function cropScreenshot( filename, imageBuffer, rect, padding ) {
|
||||
if ( padding === undefined ) {
|
||||
padding = 5;
|
||||
}
|
||||
|
||||
return Jimp.read( imageBuffer ).then( function ( jimpImage ) {
|
||||
jimpImage
|
||||
.crop(
|
||||
rect.left - padding,
|
||||
rect.top - padding,
|
||||
rect.width + ( padding * 2 ),
|
||||
rect.height + ( padding * 2 )
|
||||
)
|
||||
.write( filename );
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.$element[ 0 ],
|
||||
$( '#ca-nstab-main' )[ 0 ]
|
||||
] )
|
||||
);
|
||||
},
|
||||
0
|
||||
);
|
||||
} );
|
||||
}
|
||||
test.it( 'Citoid inspector', function () {
|
||||
runScreenshotTest( 'VisualEditor_Citoid_Inspector',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbar.tools.citefromid.onSelect();
|
||||
setTimeout( function () {
|
||||
var rect = ve.init.target.surface.context.inspectors.currentWindow.$element[ 0 ].getBoundingClientRect();
|
||||
done( {
|
||||
top: rect.top - 20,
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
height: rect.height + 20
|
||||
} );
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Headings tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_Headings',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.paragraph.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Text style tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_Formatting',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.bold.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
toolGroup.getExpandCollapseTool().onSelect();
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Indentation tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_List_and_indentation',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.bullet.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Page options list', function () {
|
||||
runScreenshotTest( 'VisualEditor_More_Settings',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.actionsToolbar.tools.advancedSettings.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ],
|
||||
// Include save button for context
|
||||
ve.init.target.toolbarSaveButton.$element[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Toolbar actions', function () {
|
||||
runScreenshotTest( 'VisualEditor_toolbar_actions',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.$actions[ 0 ]
|
||||
] )
|
||||
);
|
||||
},
|
||||
0
|
||||
);
|
||||
} );
|
||||
test.it( 'Save dialog', function () {
|
||||
runScreenshotTest( 'VisualEditor_save_dialog',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbarSaveButton.emit( 'click' );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.surface.dialogs.currentWindow.$frame[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Special character inserter', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_SpecialCharacters',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbar.tools.specialCharacter.onSelect();
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.tools.specialCharacter.$element[ 0 ],
|
||||
ve.init.target.surface.toolbarDialogs.$element[ 0 ]
|
||||
]
|
||||
) );
|
||||
}, 1000 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
test.beforeEach( function () {
|
||||
driver = new chrome.Driver();
|
||||
driver.manage().timeouts().setScriptTimeout( 20000 );
|
||||
driver.manage().window().setSize( 1200, 800 );
|
||||
} );
|
||||
|
||||
test.afterEach( function () {
|
||||
driver.quit();
|
||||
} );
|
||||
|
||||
test.it( 'Toolbar', function () {
|
||||
runScreenshotTest( 'VisualEditor_toolbar',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.$element[ 0 ],
|
||||
$( '#ca-nstab-main' )[ 0 ]
|
||||
] )
|
||||
);
|
||||
},
|
||||
0
|
||||
);
|
||||
} );
|
||||
test.it( 'Citoid inspector', function () {
|
||||
runScreenshotTest( 'VisualEditor_Citoid_Inspector',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbar.tools.citefromid.onSelect();
|
||||
setTimeout( function () {
|
||||
var rect = ve.init.target.surface.context.inspectors.currentWindow.$element[ 0 ].getBoundingClientRect();
|
||||
done( {
|
||||
top: rect.top - 20,
|
||||
left: rect.left,
|
||||
width: rect.width,
|
||||
height: rect.height + 20
|
||||
} );
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Headings tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_Headings',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.paragraph.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Text style tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_Formatting',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.bold.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
toolGroup.getExpandCollapseTool().onSelect();
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Indentation tool list', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_List_and_indentation',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.toolbar.tools.bullet.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Page options list', function () {
|
||||
runScreenshotTest( 'VisualEditor_More_Settings',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ],
|
||||
toolGroup = ve.init.target.actionsToolbar.tools.advancedSettings.toolGroup;
|
||||
toolGroup.setActive( true );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
toolGroup.$element[ 0 ],
|
||||
toolGroup.$group[ 0 ],
|
||||
// Include save button for context
|
||||
ve.init.target.toolbarSaveButton.$element[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Toolbar actions', function () {
|
||||
runScreenshotTest( 'VisualEditor_toolbar_actions',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.$actions[ 0 ]
|
||||
] )
|
||||
);
|
||||
},
|
||||
0
|
||||
);
|
||||
} );
|
||||
test.it( 'Save dialog', function () {
|
||||
runScreenshotTest( 'VisualEditor_save_dialog',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbarSaveButton.emit( 'click' );
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.surface.dialogs.currentWindow.$frame[ 0 ]
|
||||
] )
|
||||
);
|
||||
}, 500 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
test.it( 'Special character inserter', function () {
|
||||
runScreenshotTest( 'VisualEditor_Toolbar_SpecialCharacters',
|
||||
// This function is converted to a string and executed in the browser
|
||||
function () {
|
||||
var done = arguments[ arguments.length - 1 ];
|
||||
ve.init.target.toolbar.tools.specialCharacter.onSelect();
|
||||
setTimeout( function () {
|
||||
done(
|
||||
seleniumUtils.getBoundingRect( [
|
||||
ve.init.target.toolbar.tools.specialCharacter.$element[ 0 ],
|
||||
ve.init.target.surface.toolbarDialogs.$element[ 0 ]
|
||||
]
|
||||
) );
|
||||
}, 1000 );
|
||||
}
|
||||
);
|
||||
} );
|
||||
} );
|
||||
for ( i = 0, l = langs.length; i < l; i++ ) {
|
||||
runTests( langs[ i ] );
|
||||
}
|
||||
|
|
95
build/tasks/screenshotLangs.json
Normal file
95
build/tasks/screenshotLangs.json
Normal file
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"langs": [
|
||||
"ar",
|
||||
"as",
|
||||
"ast",
|
||||
"az",
|
||||
"azb",
|
||||
"bcl",
|
||||
"be-tarask",
|
||||
"bg",
|
||||
"bn",
|
||||
"bs",
|
||||
"ca",
|
||||
"cdo",
|
||||
"ceb",
|
||||
"cs",
|
||||
"cy",
|
||||
"da",
|
||||
"de",
|
||||
"diq",
|
||||
"egl",
|
||||
"el",
|
||||
"eml",
|
||||
"en",
|
||||
"en-gb",
|
||||
"eo",
|
||||
"es",
|
||||
"et",
|
||||
"eu",
|
||||
"fa",
|
||||
"fi",
|
||||
"fo",
|
||||
"fr",
|
||||
"fur",
|
||||
"fy",
|
||||
"gl",
|
||||
"he",
|
||||
"hi",
|
||||
"hif",
|
||||
"hr",
|
||||
"hu",
|
||||
"hy",
|
||||
"id",
|
||||
"ie",
|
||||
"ilo",
|
||||
"it",
|
||||
"ja",
|
||||
"jv",
|
||||
"ka",
|
||||
"km",
|
||||
"kn",
|
||||
"ko",
|
||||
"lb",
|
||||
"lt",
|
||||
"lv",
|
||||
"mai",
|
||||
"mg",
|
||||
"mk",
|
||||
"ml",
|
||||
"mr",
|
||||
"ms",
|
||||
"nap",
|
||||
"nb",
|
||||
"nl",
|
||||
"om",
|
||||
"or",
|
||||
"pa",
|
||||
"pcd",
|
||||
"pl",
|
||||
"ps",
|
||||
"pt",
|
||||
"pt-br",
|
||||
"ro",
|
||||
"ru",
|
||||
"sah",
|
||||
"sco",
|
||||
"sd",
|
||||
"sk",
|
||||
"sl",
|
||||
"sq",
|
||||
"sr",
|
||||
"su",
|
||||
"sv",
|
||||
"ta",
|
||||
"th",
|
||||
"tl",
|
||||
"tr",
|
||||
"tt",
|
||||
"uk",
|
||||
"vi",
|
||||
"xh",
|
||||
"yi",
|
||||
"zh"
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue