first
This commit is contained in:
commit
93f0567c8f
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
4
.idea/encodings.xml
Normal file
4
.idea/encodings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8" />
|
||||
</project>
|
9
.idea/i18n_language.iml
Normal file
9
.idea/i18n_language.iml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
36
.idea/inspectionProfiles/Project_Default.xml
Normal file
36
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="TOP_LEVEL_CLASS_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="INNER_CLASS_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="METHOD_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="FIELD_OPTIONS">
|
||||
<value>
|
||||
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
|
||||
<option name="REQUIRED_TAGS" value="" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="IGNORE_DEPRECATED" value="false" />
|
||||
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
|
||||
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
|
||||
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
|
||||
<option name="myAdditionalJavadocTags" value="date" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
6
.idea/misc.xml
Normal file
6
.idea/misc.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/i18n_language.iml" filepath="$PROJECT_DIR$/i18n_language.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
10
.idea/runConfigurations.xml
Normal file
10
.idea/runConfigurations.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
8
i18n_language.iml
Normal file
8
i18n_language.iml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
38
index.html
Normal file
38
index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>国际化样例</title>
|
||||
</head>
|
||||
<body>
|
||||
<form>
|
||||
<select class="lan_select">
|
||||
<option class="lan_zh" value="zh_CN" selected>中文</option>
|
||||
<option class="lan_en" value="en_US">英文</option>
|
||||
</select>
|
||||
</form>
|
||||
<!--文本自动翻译demo区域----start -->
|
||||
<form action="">
|
||||
<label data-translate="name" for="name">姓名:</label><input data-translate-placeholder="name_placeholder" type="text" placeholder="" id="name" name="name" required><br>
|
||||
<label data-translate="tel" for="phone">电话号码:</label><input data-translate-placeholder="tel_placeholder" type="tel" placeholder="" id="phone" name="phone" required><br>
|
||||
<label data-translate="email" for="email">电子邮箱:</label><input data-translate-placeholder="email_placeholder" type="email" placeholder="" id="email" name="email"><br>
|
||||
<label data-translate="address" for="address">地址:</label><input data-translate-placeholder="address_placeholder" type="text" placeholder="" id="address" name="address"><br>
|
||||
<label data-translate="choose">选择器:</label><select id="mySelect"></select><br>
|
||||
<button data-translate="save" type="button">保存</button>
|
||||
<button data-translate="reset" type="button">重置</button>
|
||||
</form>
|
||||
<!--文本自动翻译demo区域----end -->
|
||||
<!--主动翻译demo区域----start -->
|
||||
<!--<label for="name">获取当前语言包下指定的key的翻译:</label><input type="text" value="test1" id="data_key" name="data_key" ><br>-->
|
||||
<!--<button type="button" onclick="getKeyClick()">获取</button>-->
|
||||
<!--<div id="data_key_text"></div>-->
|
||||
<!--主动翻译demo区域----end -->
|
||||
<script type="application/javascript" src="js/jquery-3.7.1.min.js"></script>
|
||||
<script type="application/javascript" src="js/jquery.cookie.js"></script>
|
||||
<script type="application/javascript" src="js/jquery.i18n.properties.js"></script>
|
||||
<script type="application/javascript" src="js/language.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
2
js/jquery-3.7.1.min.js
vendored
Normal file
2
js/jquery-3.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
117
js/jquery.cookie.js
Normal file
117
js/jquery.cookie.js
Normal file
@ -0,0 +1,117 @@
|
||||
/*!
|
||||
* jQuery Cookie Plugin v1.4.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function encode(s) {
|
||||
return config.raw ? s : encodeURIComponent(s);
|
||||
}
|
||||
|
||||
function decode(s) {
|
||||
return config.raw ? s : decodeURIComponent(s);
|
||||
}
|
||||
|
||||
function stringifyCookieValue(value) {
|
||||
return encode(config.json ? JSON.stringify(value) : String(value));
|
||||
}
|
||||
|
||||
function parseCookieValue(s) {
|
||||
if (s.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape...
|
||||
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
|
||||
try {
|
||||
// Replace server-side written pluses with spaces.
|
||||
// If we can't decode the cookie, ignore it, it's unusable.
|
||||
// If we can't parse the cookie, ignore it, it's unusable.
|
||||
s = decodeURIComponent(s.replace(pluses, ' '));
|
||||
return config.json ? JSON.parse(s) : s;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function read(s, converter) {
|
||||
var value = config.raw ? s : parseCookieValue(s);
|
||||
return $.isFunction(converter) ? converter(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// Write
|
||||
|
||||
if (value !== undefined && !$.isFunction(value)) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setTime(+t + days * 864e+5);
|
||||
}
|
||||
|
||||
return (document.cookie = [
|
||||
encode(key), '=', stringifyCookieValue(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
options.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// Read
|
||||
|
||||
var result = key ? undefined : {};
|
||||
|
||||
// To prevent the for loop in the first place assign an empty array
|
||||
// in case there are no cookies at all. Also prevents odd result when
|
||||
// calling $.cookie().
|
||||
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
||||
|
||||
for (var i = 0, l = cookies.length; i < l; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var name = decode(parts.shift());
|
||||
var cookie = parts.join('=');
|
||||
|
||||
if (key && key === name) {
|
||||
// If second argument (value) is a function it's a converter...
|
||||
result = read(cookie, value);
|
||||
break;
|
||||
}
|
||||
|
||||
// Prevent storing a cookie that we couldn't decode.
|
||||
if (!key && (cookie = read(cookie)) !== undefined) {
|
||||
result[name] = cookie;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
if ($.cookie(key) === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must not alter options, thus extending a fresh object...
|
||||
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
||||
return !$.cookie(key);
|
||||
};
|
||||
|
||||
}));
|
506
js/jquery.i18n.properties.js
Normal file
506
js/jquery.i18n.properties.js
Normal file
@ -0,0 +1,506 @@
|
||||
/******************************************************************************
|
||||
* jquery.i18n.properties
|
||||
*
|
||||
* Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
|
||||
* MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
|
||||
*
|
||||
* @version 1.2.7
|
||||
* @url https://github.com/jquery-i18n-properties/jquery-i18n-properties
|
||||
* @inspiration Localisation assistance for jQuery (http://keith-wood.name/localisation.html)
|
||||
* by Keith Wood (kbwood{at}iinet.com.au) June 2007
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
(function ($) {
|
||||
|
||||
$.i18n = {};
|
||||
|
||||
/**
|
||||
* Map holding bundle keys if mode is 'map' or 'both'. Values of this can also be an
|
||||
* Object, in which case the key is a namespace.
|
||||
*/
|
||||
$.i18n.map = {};
|
||||
|
||||
var debug = function (message) {
|
||||
window.console && console.log('i18n::' + message);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load and parse message bundle files (.properties),
|
||||
* making bundles keys available as javascript variables.
|
||||
*
|
||||
* i18n files are named <name>.js, or <name>_<language>.js or <name>_<language>_<country>.js
|
||||
* Where:
|
||||
* The <language> argument is a valid ISO Language Code. These codes are the lower-case,
|
||||
* two-letter codes as defined by ISO-639. You can find a full list of these codes at a
|
||||
* number of sites, such as: http://www.loc.gov/standards/iso639-2/englangn.html
|
||||
* The <country> argument is a valid ISO Country Code. These codes are the upper-case,
|
||||
* two-letter codes as defined by ISO-3166. You can find a full list of these codes at a
|
||||
* number of sites, such as: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
|
||||
*
|
||||
* Sample usage for a bundles/Messages.properties bundle:
|
||||
* $.i18n.properties({
|
||||
* name: 'Messages',
|
||||
* language: 'en_US',
|
||||
* path: 'bundles'
|
||||
* });
|
||||
* @param name (string/string[], optional) names of file to load (eg, 'Messages' or ['Msg1','Msg2']). Defaults to "Messages"
|
||||
* @param language (string, optional) language/country code (eg, 'en', 'en_US', 'pt_BR'). if not specified, language reported by the browser will be used instead.
|
||||
* @param path (string, optional) path of directory that contains file to load
|
||||
* @param mode (string, optional) whether bundles keys are available as JavaScript variables/functions or as a map (eg, 'vars' or 'map')
|
||||
* @param debug (boolean, optional) whether debug statements are logged at the console
|
||||
* @param cache (boolean, optional) whether bundles should be cached by the browser, or forcibly reloaded on each page load. Defaults to false (i.e. forcibly reloaded)
|
||||
* @param encoding (string, optional) the encoding to request for bundles. Property file resource bundles are specified to be in ISO-8859-1 format. Defaults to UTF-8 for backward compatibility.
|
||||
* @param callback (function, optional) callback function to be called after script is terminated
|
||||
*/
|
||||
$.i18n.properties = function (settings) {
|
||||
|
||||
var defaults = {
|
||||
name: 'Messages',
|
||||
language: '',
|
||||
path: '',
|
||||
namespace: null,
|
||||
mode: 'vars',
|
||||
cache: false,
|
||||
debug: false,
|
||||
encoding: 'UTF-8',
|
||||
async: false,
|
||||
callback: null
|
||||
};
|
||||
|
||||
settings = $.extend(defaults, settings);
|
||||
|
||||
if (settings.namespace && typeof settings.namespace == 'string') {
|
||||
// A namespace has been supplied, initialise it.
|
||||
if (settings.namespace.match(/^[a-z]*$/)) {
|
||||
$.i18n.map[settings.namespace] = {};
|
||||
} else {
|
||||
debug('Namespaces can only be lower case letters, a - z');
|
||||
settings.namespace = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a trailing slash on the path
|
||||
if (!settings.path.match(/\/$/)) settings.path += '/';
|
||||
|
||||
// Try to ensure that we have at a least a two letter language code
|
||||
settings.language = this.normaliseLanguageCode(settings);
|
||||
|
||||
// Ensure an array
|
||||
var files = (settings.name && settings.name.constructor === Array) ? settings.name : [settings.name];
|
||||
|
||||
// A locale is at least a language code which means at least two files per name. If
|
||||
// we also have a country code, thats an extra file per name.
|
||||
settings.totalFiles = (files.length * 2) + ((settings.language.length >= 5) ? files.length : 0);
|
||||
if (settings.debug) {
|
||||
debug('totalFiles: ' + settings.totalFiles);
|
||||
}
|
||||
|
||||
settings.filesLoaded = 0;
|
||||
|
||||
files.forEach(function (file) {
|
||||
|
||||
var defaultFileName, shortFileName, longFileName, fileNames;
|
||||
// 1. load base (eg, Messages.properties)
|
||||
defaultFileName = settings.path + file + '.properties';
|
||||
// 2. with language code (eg, Messages_pt.properties)
|
||||
var shortCode = settings.language.substring(0, 2);
|
||||
shortFileName = settings.path + file + '_' + shortCode + '.properties';
|
||||
// 3. with language code and country code (eg, Messages_pt_BR.properties)
|
||||
if (settings.language.length >= 5) {
|
||||
var longCode = settings.language.substring(0, 5);
|
||||
longFileName = settings.path + file + '_' + longCode + '.properties';
|
||||
fileNames = [defaultFileName, shortFileName, longFileName];
|
||||
} else {
|
||||
fileNames = [defaultFileName, shortFileName];
|
||||
}
|
||||
loadAndParseFiles(fileNames, settings);
|
||||
});
|
||||
|
||||
// call callback
|
||||
if (settings.callback && !settings.async) {
|
||||
settings.callback();
|
||||
}
|
||||
}; // properties
|
||||
|
||||
/**
|
||||
* When configured with mode: 'map', allows access to bundle values by specifying its key.
|
||||
* Eg, jQuery.i18n.prop('com.company.bundles.menu_add')
|
||||
*/
|
||||
$.i18n.prop = function (key /* Add parameters as function arguments as necessary */) {
|
||||
|
||||
var args = [].slice.call(arguments);
|
||||
|
||||
var phvList, namespace;
|
||||
if (args.length == 2) {
|
||||
if ($.isArray(args[1])) {
|
||||
// An array was passed as the second parameter, so assume it is the list of place holder values.
|
||||
phvList = args[1];
|
||||
} else if (typeof args[1] === 'object') {
|
||||
// Second argument is an options object {namespace: 'mynamespace', replacements: ['egg', 'nog']}
|
||||
namespace = args[1].namespace;
|
||||
var replacements = args[1].replacements;
|
||||
args.splice(-1, 1);
|
||||
if (replacements) {
|
||||
Array.prototype.push.apply(args, replacements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var value = (namespace) ? $.i18n.map[namespace][key] : $.i18n.map[key];
|
||||
if (value === null) {
|
||||
return '[' + ((namespace) ? namespace + '#' + key : key) + ']';
|
||||
}
|
||||
|
||||
// Place holder replacement
|
||||
/**
|
||||
* Tested with:
|
||||
* test.t1=asdf ''{0}''
|
||||
* test.t2=asdf '{0}' '{1}'{1}'zxcv
|
||||
* test.t3=This is \"a quote" 'a''{0}''s'd{fgh{ij'
|
||||
* test.t4="'''{'0}''" {0}{a}
|
||||
* test.t5="'''{0}'''" {1}
|
||||
* test.t6=a {1} b {0} c
|
||||
* test.t7=a 'quoted \\ s\ttringy' \t\t x
|
||||
*
|
||||
* Produces:
|
||||
* test.t1, p1 ==> asdf 'p1'
|
||||
* test.t2, p1 ==> asdf {0} {1}{1}zxcv
|
||||
* test.t3, p1 ==> This is "a quote" a'{0}'sd{fgh{ij
|
||||
* test.t4, p1 ==> "'{0}'" p1{a}
|
||||
* test.t5, p1 ==> "'{0}'" {1}
|
||||
* test.t6, p1 ==> a {1} b p1 c
|
||||
* test.t6, p1, p2 ==> a p2 b p1 c
|
||||
* test.t6, p1, p2, p3 ==> a p2 b p1 c
|
||||
* test.t7 ==> a quoted \ s tringy x
|
||||
*/
|
||||
|
||||
var i;
|
||||
if (typeof(value) == 'string') {
|
||||
// Handle escape characters. Done separately from the tokenizing loop below because escape characters are
|
||||
// active in quoted strings.
|
||||
i = 0;
|
||||
while ((i = value.indexOf('\\', i)) != -1) {
|
||||
if (value.charAt(i + 1) == 't') {
|
||||
value = value.substring(0, i) + '\t' + value.substring((i++) + 2); // tab
|
||||
} else if (value.charAt(i + 1) == 'r') {
|
||||
value = value.substring(0, i) + '\r' + value.substring((i++) + 2); // return
|
||||
} else if (value.charAt(i + 1) == 'n') {
|
||||
value = value.substring(0, i) + '\n' + value.substring((i++) + 2); // line feed
|
||||
} else if (value.charAt(i + 1) == 'f') {
|
||||
value = value.substring(0, i) + '\f' + value.substring((i++) + 2); // form feed
|
||||
} else if (value.charAt(i + 1) == '\\') {
|
||||
value = value.substring(0, i) + '\\' + value.substring((i++) + 2); // \
|
||||
} else {
|
||||
value = value.substring(0, i) + value.substring(i + 1); // Quietly drop the character
|
||||
}
|
||||
}
|
||||
|
||||
// Lazily convert the string to a list of tokens.
|
||||
var arr = [], j, index;
|
||||
i = 0;
|
||||
while (i < value.length) {
|
||||
if (value.charAt(i) == '\'') {
|
||||
// Handle quotes
|
||||
if (i == value.length - 1) {
|
||||
value = value.substring(0, i); // Silently drop the trailing quote
|
||||
} else if (value.charAt(i + 1) == '\'') {
|
||||
value = value.substring(0, i) + value.substring(++i); // Escaped quote
|
||||
} else {
|
||||
// Quoted string
|
||||
j = i + 2;
|
||||
while ((j = value.indexOf('\'', j)) != -1) {
|
||||
if (j == value.length - 1 || value.charAt(j + 1) != '\'') {
|
||||
// Found start and end quotes. Remove them
|
||||
value = value.substring(0, i) + value.substring(i + 1, j) + value.substring(j + 1);
|
||||
i = j - 1;
|
||||
break;
|
||||
} else {
|
||||
// Found a double quote, reduce to a single quote.
|
||||
value = value.substring(0, j) + value.substring(++j);
|
||||
}
|
||||
}
|
||||
|
||||
if (j == -1) {
|
||||
// There is no end quote. Drop the start quote
|
||||
value = value.substring(0, i) + value.substring(i + 1);
|
||||
}
|
||||
}
|
||||
} else if (value.charAt(i) == '{') {
|
||||
// Beginning of an unquoted place holder.
|
||||
j = value.indexOf('}', i + 1);
|
||||
if (j == -1) {
|
||||
i++; // No end. Process the rest of the line. Java would throw an exception
|
||||
} else {
|
||||
// Add 1 to the index so that it aligns with the function arguments.
|
||||
index = parseInt(value.substring(i + 1, j));
|
||||
if (!isNaN(index) && index >= 0) {
|
||||
// Put the line thus far (if it isn't empty) into the array
|
||||
var s = value.substring(0, i);
|
||||
if (s !== "") {
|
||||
arr.push(s);
|
||||
}
|
||||
// Put the parameter reference into the array
|
||||
arr.push(index);
|
||||
// Start the processing over again starting from the rest of the line.
|
||||
i = 0;
|
||||
value = value.substring(j + 1);
|
||||
} else {
|
||||
i = j + 1; // Invalid parameter. Leave as is.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
} // while
|
||||
|
||||
// Put the remainder of the no-empty line into the array.
|
||||
if (value !== "") {
|
||||
arr.push(value);
|
||||
}
|
||||
value = arr;
|
||||
|
||||
// Make the array the value for the entry.
|
||||
if (namespace) {
|
||||
$.i18n.map[settings.namespace][key] = arr;
|
||||
} else {
|
||||
$.i18n.map[key] = arr;
|
||||
}
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
return "";
|
||||
}
|
||||
if (value.length == 1 && typeof(value[0]) == "string") {
|
||||
return value[0];
|
||||
}
|
||||
|
||||
var str = "";
|
||||
for (i = 0, j = value.length; i < j; i++) {
|
||||
if (typeof(value[i]) == "string") {
|
||||
str += value[i];
|
||||
} else if (phvList && value[i] < phvList.length) {
|
||||
// Must be a number
|
||||
str += phvList[value[i]];
|
||||
} else if (!phvList && value[i] + 1 < args.length) {
|
||||
str += args[value[i] + 1];
|
||||
} else {
|
||||
str += "{" + value[i] + "}";
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
function callbackIfComplete(settings) {
|
||||
|
||||
if (settings.debug) {
|
||||
debug('callbackIfComplete()');
|
||||
debug('totalFiles: ' + settings.totalFiles);
|
||||
debug('filesLoaded: ' + settings.filesLoaded);
|
||||
}
|
||||
|
||||
if (settings.async) {
|
||||
if (settings.filesLoaded === settings.totalFiles) {
|
||||
if (settings.callback) {
|
||||
settings.callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadAndParseFiles(fileNames, settings) {
|
||||
|
||||
if (settings.debug) debug('loadAndParseFiles');
|
||||
|
||||
if (fileNames !== null && fileNames.length > 0) {
|
||||
loadAndParseFile(fileNames[0], settings, function () {
|
||||
fileNames.shift();
|
||||
loadAndParseFiles(fileNames,settings);
|
||||
});
|
||||
} else {
|
||||
callbackIfComplete(settings);
|
||||
}
|
||||
}
|
||||
|
||||
/** Load and parse .properties files */
|
||||
function loadAndParseFile(filename, settings, nextFile) {
|
||||
|
||||
if (settings.debug) {
|
||||
debug('loadAndParseFile(\'' + filename +'\')');
|
||||
debug('totalFiles: ' + settings.totalFiles);
|
||||
debug('filesLoaded: ' + settings.filesLoaded);
|
||||
}
|
||||
|
||||
if (filename !== null && typeof filename !== 'undefined') {
|
||||
$.ajax({
|
||||
url: filename,
|
||||
async: settings.async,
|
||||
cache: settings.cache,
|
||||
dataType: 'text',
|
||||
success: function (data, status) {
|
||||
|
||||
if (settings.debug) {
|
||||
debug('Succeeded in downloading ' + filename + '.');
|
||||
debug(data);
|
||||
}
|
||||
|
||||
parseData(data, settings);
|
||||
nextFile();
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
|
||||
if (settings.debug) {
|
||||
debug('Failed to download or parse ' + filename + '. errorThrown: ' + errorThrown);
|
||||
}
|
||||
if (jqXHR.status === 404) {
|
||||
settings.totalFiles -= 1;
|
||||
}
|
||||
nextFile();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse .properties files */
|
||||
function parseData(data, settings) {
|
||||
|
||||
var parsed = '';
|
||||
var lines = data.split(/\n/);
|
||||
var regPlaceHolder = /(\{\d+})/g;
|
||||
var regRepPlaceHolder = /\{(\d+)}/g;
|
||||
var unicodeRE = /(\\u.{4})/ig;
|
||||
for (var i=0,j=lines.length;i<j;i++) {
|
||||
var line = lines[i];
|
||||
|
||||
line = line.trim();
|
||||
if (line.length > 0 && line.match("^#") != "#") { // skip comments
|
||||
var pair = line.split('=');
|
||||
if (pair.length > 0) {
|
||||
/** Process key & value */
|
||||
var name = decodeURI(pair[0]).trim();
|
||||
var value = pair.length == 1 ? "" : pair[1];
|
||||
// process multi-line values
|
||||
while (value.search(/\\$/) != -1) {
|
||||
value = value.substring(0, value.length - 1);
|
||||
value += lines[++i].trimRight();
|
||||
}
|
||||
// Put values with embedded '='s back together
|
||||
for (var s = 2; s < pair.length; s++) {
|
||||
value += '=' + pair[s];
|
||||
}
|
||||
value = value.trim();
|
||||
|
||||
/** Mode: bundle keys in a map */
|
||||
if (settings.mode == 'map' || settings.mode == 'both') {
|
||||
// handle unicode chars possibly left out
|
||||
var unicodeMatches = value.match(unicodeRE);
|
||||
if (unicodeMatches) {
|
||||
unicodeMatches.forEach(function (match) {
|
||||
value = value.replace(match, unescapeUnicode(match));
|
||||
});
|
||||
}
|
||||
// add to map
|
||||
if (settings.namespace) {
|
||||
$.i18n.map[settings.namespace][name] = value;
|
||||
} else {
|
||||
$.i18n.map[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/** Mode: bundle keys as vars/functions */
|
||||
if (settings.mode == 'vars' || settings.mode == 'both') {
|
||||
value = value.replace(/"/g, '\\"'); // escape quotation mark (")
|
||||
|
||||
// make sure namespaced key exists (eg, 'some.key')
|
||||
checkKeyNamespace(name);
|
||||
|
||||
// value with variable substitutions
|
||||
if (regPlaceHolder.test(value)) {
|
||||
var parts = value.split(regPlaceHolder);
|
||||
// process function args
|
||||
var first = true;
|
||||
var fnArgs = '';
|
||||
var usedArgs = [];
|
||||
parts.forEach(function (part) {
|
||||
|
||||
if (regPlaceHolder.test(part) && (usedArgs.length === 0 || usedArgs.indexOf(part) == -1)) {
|
||||
if (!first) {
|
||||
fnArgs += ',';
|
||||
}
|
||||
fnArgs += part.replace(regRepPlaceHolder, 'v$1');
|
||||
usedArgs.push(part);
|
||||
first = false;
|
||||
}
|
||||
});
|
||||
parsed += name + '=function(' + fnArgs + '){';
|
||||
// process function body
|
||||
var fnExpr = '"' + value.replace(regRepPlaceHolder, '"+v$1+"') + '"';
|
||||
parsed += 'return ' + fnExpr + ';' + '};';
|
||||
// simple value
|
||||
} else {
|
||||
parsed += name + '="' + value + '";';
|
||||
}
|
||||
} // END: Mode: bundle keys as vars/functions
|
||||
} // END: if(pair.length > 0)
|
||||
} // END: skip comments
|
||||
}
|
||||
eval(parsed);
|
||||
settings.filesLoaded += 1;
|
||||
}
|
||||
|
||||
/** Make sure namespace exists (for keys with dots in name) */
|
||||
// TODO key parts that start with numbers quietly fail. i.e. month.short.1=Jan
|
||||
function checkKeyNamespace(key) {
|
||||
|
||||
var regDot = /\./;
|
||||
if (regDot.test(key)) {
|
||||
var fullname = '';
|
||||
var names = key.split(/\./);
|
||||
for (var i=0,j=names.length;i<j;i++) {
|
||||
var name = names[i];
|
||||
|
||||
if (i > 0) {
|
||||
fullname += '.';
|
||||
}
|
||||
|
||||
fullname += name;
|
||||
if (eval('typeof ' + fullname + ' == "undefined"')) {
|
||||
eval(fullname + '={};');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure language code is in the format aa_AA. */
|
||||
$.i18n.normaliseLanguageCode = function (settings) {
|
||||
|
||||
var lang = settings.language;
|
||||
if (!lang || lang.length < 2) {
|
||||
if (settings.debug) debug('No language supplied. Pulling it from the browser ...');
|
||||
lang = (navigator.languages && navigator.languages.length > 0) ? navigator.languages[0]
|
||||
: (navigator.language || navigator.userLanguage /* IE */ || 'en');
|
||||
if (settings.debug) debug('Language from browser: ' + lang);
|
||||
}
|
||||
|
||||
lang = lang.toLowerCase();
|
||||
lang = lang.replace(/-/,"_"); // some browsers report language as en-US instead of en_US
|
||||
if (lang.length > 3) {
|
||||
lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
|
||||
}
|
||||
return lang;
|
||||
};
|
||||
|
||||
/** Unescape unicode chars ('\u00e3') */
|
||||
function unescapeUnicode(str) {
|
||||
|
||||
// unescape unicode codes
|
||||
var codes = [];
|
||||
var code = parseInt(str.substr(2), 16);
|
||||
if (code >= 0 && code < Math.pow(2, 16)) {
|
||||
codes.push(code);
|
||||
}
|
||||
// convert codes to text
|
||||
return codes.reduce(function (acc, val) { return acc + String.fromCharCode(val); }, '');
|
||||
}
|
||||
}) (jQuery);
|
160
js/language.js
Normal file
160
js/language.js
Normal file
@ -0,0 +1,160 @@
|
||||
//网站默认语言标识
|
||||
var languageKey = "zh_CN";
|
||||
/*
|
||||
设置一下网站支持的语言种类
|
||||
zh_CN(中文简体)、en_US(英语)
|
||||
*/
|
||||
var languageArray = ['zh_CN', 'en_US'];
|
||||
|
||||
/**
|
||||
* 页面初始化执行方法
|
||||
*/
|
||||
$(document).ready(async function () {
|
||||
//1.cookie是否存在
|
||||
if ($.cookie("userLanguage")) {
|
||||
languageKey = $.cookie("userLanguage");
|
||||
} else {
|
||||
//2.1 获取用户设置的浏览器语言
|
||||
var webLanguage = $.i18n.normaliseLanguageCode({});
|
||||
var charSize = $.inArray(webLanguage, languageArray);
|
||||
if (charSize > -1) {
|
||||
languageKey = webLanguage;
|
||||
// 存到缓存中
|
||||
$.cookie("userLanguage", webLanguage, {
|
||||
expires: 7
|
||||
});
|
||||
} else {
|
||||
//不支持的语言,使用默认中文
|
||||
}
|
||||
}
|
||||
$(".lan_select").val(languageKey)
|
||||
|
||||
/**
|
||||
* js中调用翻译代码示例(方法一)
|
||||
*/
|
||||
// let selectElement = document.getElementById('mySelect');
|
||||
// // 定义选项数据
|
||||
// let optionsData = [
|
||||
// {
|
||||
// value: '1',
|
||||
// //这里以js文件中name为例,进行翻译
|
||||
// text: 'tel'
|
||||
// },
|
||||
// {
|
||||
// value: '2',
|
||||
// text: 'name'
|
||||
// },
|
||||
// ];
|
||||
// console.log(optionsData,76)
|
||||
// // 填充<select>元素
|
||||
// optionsData.forEach(option => {
|
||||
// const newOption = document.createElement('option');
|
||||
// //给元素添加翻译标识
|
||||
// newOption.setAttribute("data-translate",option.text)
|
||||
// newOption.value = option.value;
|
||||
// newOption.textContent = option.text;
|
||||
// selectElement.appendChild(newOption);
|
||||
// });
|
||||
loadProperties(languageKey);
|
||||
jsDemo();
|
||||
});
|
||||
|
||||
/**
|
||||
* js中调用翻译代码示例(方法二)
|
||||
*/
|
||||
async function jsDemo() {
|
||||
let selectElement = document.getElementById('mySelect');
|
||||
// 定义选项数据
|
||||
let optionsData = [
|
||||
{
|
||||
value: '1',
|
||||
//这里以js文件中name为例,进行翻译
|
||||
text: await getTextByTranslationKey('tel')
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
text: await getTextByTranslationKey('name')
|
||||
},
|
||||
];
|
||||
// 填充<select>元素
|
||||
optionsData.forEach(option => {
|
||||
const newOption = document.createElement('option');
|
||||
//给元素添加翻译标识
|
||||
// newOption.setAttribute("data-translate",option.text)
|
||||
newOption.value = option.value;
|
||||
newOption.textContent = option.text;
|
||||
selectElement.appendChild(newOption);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 语言切换事件监听,切换后存cookie
|
||||
*/
|
||||
$(".lan_select").change(function () {
|
||||
languageKey = $(".lan_select").val()
|
||||
$.cookie("userLanguage", languageKey, {
|
||||
expires: 7
|
||||
});
|
||||
loadProperties(languageKey);
|
||||
location.reload();
|
||||
});
|
||||
|
||||
/**
|
||||
* 自动翻译页面内容
|
||||
* @param type 语言包标识
|
||||
*/
|
||||
function loadProperties(type) {
|
||||
$.i18n.properties({
|
||||
name: 'strings', // 资源文件名称
|
||||
path: 'languages/', // 资源文件所在目录路径
|
||||
mode: 'map', // 模式:变量或 Map
|
||||
language: type, // 对应的语言
|
||||
cache: false,
|
||||
encoding: 'UTF-8',
|
||||
callback: function () {
|
||||
// 回调方法
|
||||
$('[data-translate]').each(function () {
|
||||
//遍历每一个需要翻译的元素,本次翻译text
|
||||
var translationKey = $(this).data('translate');
|
||||
$(this).text($.i18n.prop(translationKey));
|
||||
});
|
||||
$('[data-translate-placeholder]').each(function () {
|
||||
//遍历每一个需要翻译的元素,本次翻译placeholder
|
||||
var translationKey = $(this).data('translate-placeholder');
|
||||
$(this).attr("placeholder", $.i18n.prop(translationKey))
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定翻译内容按钮点击执行方法---临时演示用,可以删掉
|
||||
*/
|
||||
function getKeyClick() {
|
||||
getTextByTranslationKey($("#data_key").val())
|
||||
}
|
||||
/**
|
||||
* 主动翻译指定key
|
||||
* @param translationKey 需要翻译的key
|
||||
*/
|
||||
function getTextByTranslationKey(translationKey) {
|
||||
return new Promise((resolve, reject) => {
|
||||
$.i18n.properties({
|
||||
name: 'strings', // 资源文件名称
|
||||
path: 'languages/', // 资源文件所在目录路径
|
||||
mode: 'map', // 模式:变量或 Map
|
||||
language: languageKey, // 对应的语言
|
||||
cache: false,
|
||||
encoding: 'UTF-8',
|
||||
callback: function () {
|
||||
// 回调方法
|
||||
//这一行演示用,可以删掉
|
||||
$("#data_key_text").text($.i18n.prop(translationKey))
|
||||
resolve($.i18n.prop(translationKey))
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
13
languages/strings_en_US.properties
Normal file
13
languages/strings_en_US.properties
Normal file
@ -0,0 +1,13 @@
|
||||
name=name:
|
||||
name_placeholder=Please enter name
|
||||
tel=tel:
|
||||
tel_placeholder=Please enter tel
|
||||
email=email:
|
||||
email_placeholder=Please enter email
|
||||
address=address:
|
||||
address_placeholder= Please enter address
|
||||
choose=choose:
|
||||
choose_placeholder=Please choose
|
||||
save=save
|
||||
reset=reset
|
||||
test1=Specify translation content
|
13
languages/strings_zh_CN.properties
Normal file
13
languages/strings_zh_CN.properties
Normal file
@ -0,0 +1,13 @@
|
||||
name=姓名:
|
||||
name_placeholder=请输入姓名
|
||||
tel=电话:
|
||||
tel_placeholder=请输入电话
|
||||
email=邮箱:
|
||||
email_placeholder=请输入邮箱
|
||||
address=地址:
|
||||
address_placeholder=请输入地址
|
||||
choose=选择器:
|
||||
choose_placeholder=请选择
|
||||
save=保存
|
||||
reset=重置
|
||||
test1=指定翻译内容
|
Loading…
Reference in New Issue
Block a user