Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support i18n #461

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,443 changes: 1,423 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
"update_engine_exports": "run-python3 tools/update_engine_exports.py"
},
"devDependencies": {
"@babel/eslint-parser": "7.24.1",
"@babel/plugin-syntax-import-assertions": "7.24.1",
"@rollup/plugin-json": "6.1.0",
"@types/node": "^20.1.0",
"esbuild": "^0.19.0",
"eslint": "^8.29.0",
Expand All @@ -70,12 +73,13 @@
"rollup": "^4.0.2",
"run-python3": "^0.0.5",
"svgo": "^3.0.2",
"typescript": "^5.0.4"
"typescript": "5.4.4"
},
"dependencies": {
"@simonwep/pickr": "1.9.0",
"draco3d": "1.5.6",
"fflate": "0.8.1",
"i18next": "23.10.1",
"occt-import-js": "0.0.21",
"rhino3dm": "8.0.1",
"three": "0.158.0",
Expand All @@ -98,9 +102,16 @@
"WebIFC": "readonly",
"occtimportjs": "readonly"
},
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
"sourceType": "module",
"requireConfigFile": false,
"babelOptions": {
"plugins": [
"@babel/plugin-syntax-import-assertions"
]
}
},
"plugins": [
"unused-imports"
Expand Down
4 changes: 3 additions & 1 deletion source/engine/import/importerbim.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { ColorToMaterialConverter } from './importerutils.js';
import { Property, PropertyGroup, PropertyType } from '../model/property.js';
import { Unit } from '../model/unit.js';

import { t } from '../../website/i18next.js';

export class ImporterBim extends ImporterBase
{
constructor ()
Expand Down Expand Up @@ -170,7 +172,7 @@ export class ImporterBim extends ImporterBase
}

let info = source.info;
let propertyGroup = new PropertyGroup ('Info');
let propertyGroup = new PropertyGroup (t('Info'));
AddProperty (propertyGroup, 'Guid', source.guid);
AddProperty (propertyGroup, 'Type', source.type);
for (let propertyName in info) {
Expand Down
10 changes: 6 additions & 4 deletions source/engine/import/importergltf.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { Property, PropertyGroup, PropertyType } from '../model/property.js';
import { Triangle } from '../model/triangle.js';
import { ImporterBase } from './importerbase.js';

import { t } from '../../website/i18next.js';

const GltfComponentType =
{
BYTE : 5120,
Expand Down Expand Up @@ -641,7 +643,7 @@ export class ImporterGltf extends ImporterBase
}
}

this.ImportProperties (this.model, gltf.asset, 'Asset properties');
this.ImportProperties (this.model, gltf.asset, t('Asset properties'));
this.ImportScene (gltf);
}

Expand Down Expand Up @@ -809,7 +811,7 @@ export class ImporterGltf extends ImporterBase
this.ImportPrimitive (gltf, primitive, mesh);
}

this.ImportProperties (mesh, gltfMesh.extras, 'Mesh properties');
this.ImportProperties (mesh, gltfMesh.extras, t('Mesh properties'));
}

ImportPrimitive (gltf, primitive, mesh)
Expand Down Expand Up @@ -1006,7 +1008,7 @@ export class ImporterGltf extends ImporterBase
this.ImportNode (gltf, gltfNode, rootNode);
}

this.ImportProperties (this.model, scene.extras, 'Scene properties');
this.ImportProperties (this.model, scene.extras, t('Scene properties'));
}

ImportNode (gltf, gltfNode, parentNode)
Expand Down Expand Up @@ -1058,7 +1060,7 @@ export class ImporterGltf extends ImporterBase

if (gltfNode.mesh !== undefined) {
let mesh = this.model.GetMesh (gltfNode.mesh);
this.ImportProperties (mesh, gltfNode.extras, 'Node properties');
this.ImportProperties (mesh, gltfNode.extras, t('Node properties'));
node.AddMeshIndex (gltfNode.mesh);
}
}
Expand Down
6 changes: 6 additions & 0 deletions source/engine/parameters/parameterlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,12 @@ export class ParameterListParser
return ParameterConverter.StringToEdgeSettings (edgeSettingsParams);
}

GetLocale ()
{
let localeParams = this.GetKeywordParams ('locale');
return localeParams;
}

GetKeywordParams (keyword)
{
if (this.paramList === null || this.paramList.length === 0) {
Expand Down
4 changes: 3 additions & 1 deletion source/website/dialogs.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { AddDiv } from '../engine/viewer/domutils.js';
import { ButtonDialog, ListPopup } from './dialog.js';

import { t } from './i18next.js';

export function ShowMessageDialog (title, message, subMessage)
{
let dialog = new ButtonDialog ();
let contentDiv = dialog.Init (title, [
{
name : 'OK',
name : t('OK'),
onClick () {
dialog.Close ();
}
Expand Down
18 changes: 10 additions & 8 deletions source/website/exportdialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { DownloadArrayBufferAsFile } from './utils.js';
import { CookieGetStringVal, CookieSetStringVal } from './cookiehandler.js';
import { HandleEvent } from './eventhandler.js';

import { t } from './i18next.js';

import * as fflate from 'fflate';

function AddSelectWithCookieSave (parentElement, cookieKey, options, defaultSelectedIndex, onChange)
Expand Down Expand Up @@ -53,8 +55,8 @@ class ModelExporterUI
return AddSelectWithCookieSave (parameterValueDiv, cookieKey, values, defaultIndex);
}

this.visibleOnlySelect = AddSelectItem (parametersDiv, 'Scope', 'ov_last_scope', ['Entire Model', 'Visible Only'], 1);
this.rotationSelect = AddSelectItem (parametersDiv, 'Rotation', 'ov_last_rotation', ['No Rotation', '-90 Degrees', '90 Degrees'], 0);
this.visibleOnlySelect = AddSelectItem (parametersDiv, t('Scope'), 'ov_last_scope', [t('Entire Model'), t('Visible Only')], 1);
this.rotationSelect = AddSelectItem (parametersDiv, t('Rotation'), 'ov_last_rotation', [t('No Rotation'), t('-90 Degrees'), t('90 Degrees')], 0);
}

ExportModel (model, callbacks)
Expand All @@ -77,15 +79,15 @@ class ModelExporterUI
let exporterModel = new ExporterModel (model, settings);
if (exporterModel.MeshInstanceCount () === 0) {
ShowMessageDialog (
'Export Failed',
t('Export Failed'),
'The model doesn\'t contain any meshes.',
null
);
return;
}

let progressDialog = new ProgressDialog ();
progressDialog.Init ('Exporting Model');
progressDialog.Init (t('Exporting Model'));
progressDialog.Open ();

RunTaskAsync (() => {
Expand Down Expand Up @@ -142,24 +144,24 @@ class ExportDialog
Open (model, viewer)
{
let mainDialog = new ButtonDialog ();
let contentDiv = mainDialog.Init ('Export', [
let contentDiv = mainDialog.Init (t('Export'), [
{
name : 'Close',
name : t('Close'),
subClass : 'outline',
onClick () {
mainDialog.Close ();
}
},
{
name : 'Export',
name : t('Export'),
onClick : () => {
mainDialog.Close ();
this.ExportFormat (model, viewer);
}
}
]);

let text = 'Select the format from the list below, and adjust the settings of the selected format.';
let text = t('Select the format from the list below, and adjust the settings of the selected format.');
AddDiv (contentDiv, 'ov_dialog_section', text);

let formatRow = AddDiv (contentDiv, 'ov_dialog_row');
Expand Down
6 changes: 6 additions & 0 deletions source/website/hashhandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ export class HashHandler
return parser.GetEdgeSettings ();
}

GetLocaleFromHash ()
{
let parser = CreateUrlParser (this.GetHash ());
return parser.GetLocale ();
}

GetHash ()
{
return window.location.hash.substring (1);
Expand Down
35 changes: 35 additions & 0 deletions source/website/i18next.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import i18next from 'i18next';
import { CookieGetStringVal } from './cookiehandler.js';
import { HashHandler } from './hashhandler.js';
import zhCN from './locale/zh-CN.json' assert { type: 'json' };

const hashHandler = new HashHandler();

const defaultLang =
typeof navigator !== 'undefined'
? hashHandler.GetLocaleFromHash() ||
CookieGetStringVal('ov_language', navigator.language)
: '';

i18next.init({
lng: defaultLang, // if you're u sing a language detector, do not define the lng option
debug: false,
supportedLngs: ['zh-CN'],
resources: {
'zh-CN': { translation: zhCN },
},
});

const t = i18next.t;

export { i18next, t };

if (typeof document !== 'undefined') {
// translate HTML
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('[data-i18n]').forEach((ele) => {
const translateKey = ele.getAttribute('data-i18n') || ele.textContent;
ele.textContent = t(translateKey);
});
});
}
127 changes: 127 additions & 0 deletions source/website/locale/zh-CN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"Drag and drop 3D models here.": "拖拽3D模型到这里",
"Check an example file:": "查看示例文件:",
"Fit model to window": "自适应窗口",
"Set Y axis as up vector": "Y轴向上",
"Set Z axis as up vector": "Z轴向上",
"Flip up vector": "上下翻转",
"Fixed up vector": "固定移动",
"Free orbit": "自由移动",
"Perspective camera": "透视相机",
"Orthographic camera": "正交相机",
"Files": "文件",
"Materials": "材质",
"Meshes": "网格",
"Details": "详情",
"Calculate": "计算",
"Model Display": "模型显示设置",
"Background Color": "背景颜色",
"Show Edges": "显示边缘",
"Edge Color": "边缘颜色",
"Edge Angle Threshold": "边角阈值",
"Reset to Default": "恢复默认设置",
"Importing Model": "模型导入中...",
"Visualizing Model": "模型渲染中...",
"Something went wrong": "出错了!",
"No importable file found.": "无法加载要导入的文件",
"Failed to load file for import.": "无法加载要导入的文件",
"The remote server refused to fulfill the request. Check if the url is correct, and make sure that CORS requests are allowed on the remote server.": "远程服务器拒绝完成请求。请检查模型url是否正确,并确保远程服务器上允许 CORS 请求。",
"Failed to import model.": "模型载入失败",
"Unknown error.": "未知错误",
"OK": "确定",
"Cancel": "取消",
"Close": "关闭",
"Create": "创建",
"Settings": "设置",
"Light mode": "明亮模式",
"Dark mode": "暗黑模式",
"Import Settings": "导入设置",
"Default Color": "默认颜色",
"Default Line Color": "默认线条颜色",
"Background Image": "背景图片",
"No Name": "未命名",
"Info": "信息",
"Missing Files": "缺失的文件",
"Available Files": "可用的文件",
"Open from your device": "从设备中打开",
"Open from url": "从url中打开",
"Here you can load models based on their urls. You can add more lines if your model builds up from multiple files.": "在这里,您可以根据url加载模型。如果您的模型由多个文件组成,您可以添加多行。",
"Measure": "测量",
"Download": "下载",
"Export": "导出",
"Share": "分享",
"Create snapshot": "创建快照",
"Flat list": "列表视图",
"Tree view": "树形视图",
"Expand all": "展开全部",
"Collapse all": "折叠全部",
"Show/hide meshes": "显示/隐藏网格",
"Fit meshes to window": "网格适应窗口",
"Create Snapshot": "创建快照",
"Small": "小",
"Medium": "中",
"Large": "大",
"Custom": "自定义",
"Width": "宽",
"Height": "高",
"Transparent background": "透明背景",
"Sharing Link": "分享链接",
"Embedding Code": "嵌入代码",
"Use customized settings": "使用自定义设置",
"Copy": "复制",
"Copied": "已复制",
"Select the format from the list below, and adjust the settings of the selected format.": "从下面的列表中选择格式,并调整所选格式的设置。",
"Export Failed": "导出失败",
"Exporting Model": "模型导出中",
"Scope": "范围",
"Rotation": "旋转",
"Entire Model": "整个模型",
"Visible Only": "仅可见部分",
"No Rotation": "无旋转",
"-90 Degrees": "-90度",
"90 Degrees": "90度",
"Select a point.": "选择一个点",
"Select another point.": "选择另一个点",
"Distance of points": "两点之间的距离",
"Distance of parallel faces": "平行面之间的距离",
"Angle of faces": "面之间的角度",
"Cookies Policy": "Cookies政策",
"This website uses cookies to offer you better user experience. See the details at the {{- link }} page.": "本网站使用cookies为您提供更好的用户体验。请查看{{- link }}页面的详细信息。",
"Accept": "接受",
"Vertices": "点",
"Lines": "线",
"Triangles": "三角形",
"Unit": "单位",
"Millimeter": "毫米",
"Centimeter": "厘米",
"Meter": "米",
"Inch": "英寸",
"Foot": "英尺",
"Size X": "尺寸 X",
"Size Y": "尺寸 Y",
"Size Z": "尺寸 Z",
"Volume": "体积",
"Surface": "表面积",
"Unknown": "未知",
"Asset properties": "资源属性",
"Mesh properties": "网格属性",
"Scene properties": "场景属性",
"Node properties": "节点属性",
"Default": "默认",
"Model": "来自模型",
"Source": "来源",
"Type": "类型",
"Color": "颜色",
"Ambient": "环境光",
"Specular": "高光",
"Metalness": "金属度",
"Roughness": "粗糙度",
"Opacity": "透明度",
"Diffuse Map": "漫反射贴图",
"Bump Map": "凹凸贴图",
"Normal Map": "法线贴图",
"Emissive Map": "自发光贴图",
"Specular Map": "高光贴图",
"Metallic Map": "金属贴图",
"Please wait...": "请稍等..."
}
Loading